Starter
OAuth2.0 是一种协议,提供认证和授权的标准 SpringSecurity,Shiro等框架有自己的实现
OAuth2.0 涵盖两个服务,可放于一个应用程序中实现,也可用于 1(认证服务)+ N(资源服务)分别部署
- 认证服务 Authorization Server:认证合法性,颁发token,即包含两个必须要实现的endpoints
- AuthorizationEndpoint 用于认证的请求,默认URL:
/auth/authorize
- TokenEndpoint 用于获取token的请求,默认URL:
/auth/token
- AuthorizationEndpoint 用于认证的请求,默认URL:
- 资源服务 Resource Server:拦截保护资源,token鉴权
- OAuth2AuthenticationProcessingFilter 拦截鉴权token
配置:认证授权服务
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
需配置以下三个对象:
- ClientDetailsServiceConfigurer 配置哪些客户端可使用此服务
- AuthorizationServerEndpointsConfigurer 配置获取token的访问端点,token服务(token如何发放,存储等)
- 通过以下属性决定支持的授权类型(Grant Types)
- authenticationManager 为"password"模式服务
- userDetailsService
- authorizationCodeServices 为“authorization_code"授权码模式服务
- implicitGrantService 设置隐式授权模式
- tokenGranter 自定义模式使用
- pathMapping()配置端点URL,默认:
- /oauth/authorize 认证
- /oauth/token 获取token
- /oauth/confirm_access 确认授权提交
- /oauth/error 错误
- /oauth/check_token 校验token
- /oauth/token_key 公有密钥
- 通过以下属性决定支持的授权类型(Grant Types)
- AuthorizationServerSecurityConfigurer 配置token端点的安全约束(授权哪些可以访问token端点)
认证授权模式:
- 授权码模式 authorization_code
- 简化模式 implicit
- 密码模式 password
客户端模式 client_credentials
认证:验证账号密码
- 授权:这个角色能操作哪些数据
- 角色:人(运营,编辑,管理员,...)/ 系统 (日志系统,监控系统,...)/ 时间(定时清理,...)
- 权限控制模型:
- RBAC 基于角色的访问控制 (Role Based Access Control)
- ACL 访问控制列表
- ABAC 基于属性
- PBAC 基于策略
配置:资源服务
public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
ResourceServerTokenServices 验证token
- DefaultTokenService 在资源服务器本地,配置token存储,解码等服务(认证和资源服务在同一个应用程序时使用)
- RemoteTokenService 资源服务器通过Http方式,请求认证服点(/oauth/check_token)来完成解码token(认证和资源服务不在同一个应用程序时使用)
Doc
https://spring.io/projects/spring-security-oauth https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
Sample
认证服务: 基于内存方式
application.yml
server: port: 5000 servlet: context-path: /dear-auth
配置OAuth认证授权服务器
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{ /// 配置Client @Autowired private PasswordEncoder passwordEncoder; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 使用内存方式 clients.inMemory() .withClient("DearApp")//客户端id .secret(passwordEncoder.encode("DearApp")) .authorizedGrantTypes("authorization_code", "password","refresh_token", ) // 该客户端允许的授权类型(authorization_code,password,client_credentials,implicit,refresh_token) .scopes("app")//允许的授权范围,名称自定义,是个标识,必填 .redirectUris("http://www.baidu.com") //验证回调地址 ; } }
配置Security(提供认证授权页面进行认证:
http://localhost:5000/dear-auth/login
)@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true) class WebSecurityConfig extends WebSecurityConfigurerAdapter { // 采用bcrypt对密码进行编码 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin").password(passwordEncoder().encode("123")).roles("ADMIN") .and() .withUser("user").password(passwordEncoder().encode("123")).roles("USER") ; } }
AuthApp.java
@SpringBootApplication public class AuthApp { public static void main(String[] args){ SpringApplication.run(AuthApp.class); } }
测试:
- Browser visit: GET
http://localhost:5000/dear-auth/oauth/authorize?client_id=DearApp&response_type=code
- 默认跳转到认证页面
http://localhost:5000/dear-auth/login
,输入账户密码(admin,123)进行验证 - 认证成功后,跳转回授权页面
http://localhost:5000/dear-auth/oauth/authorize?client_id=DearApp&response_type=code
,选择是否同意授权 - 同意授权后,则跳转到 redirect_uri+code
https://www.baidu.com/?code=3Tgl8q
,即获得授权码3Tgl8q
- 默认跳转到认证页面
获取token
使用cmd命令:
> curl -d 'grant_type=authorization_code&code=3Tgl8q' -X POST http://DearApp:DearApp@localhost:5000/dear-auth/oauth/token -i HTTP/1.1 200 Cache-Control: no-store Pragma: no-cache X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block X-Frame-Options: DENY Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Thu, 21 Jan 2021 13:32:58 GMT {"access_token":"e775d74e-9e8c-437d-974d-6d8286b37396","token_type":"bearer","refresh_token":"6b9f3fa3-143b-47a0-b356-3f577e8c7fed","expires_in":43199,"scope":"app"}
- 使用Postman
- POST
http://DearApp:DearApp@localhost:5000/dear-auth/oauth/token
- Authorization -> Basic Auth -> Username(DearApp) & Password(DearApp)
- Body -> x-www-form-urlencoded -> grant_type & code
- POST
- 注意:
- 授权码使用一次就失效了
- 获取token的body参数 redirect_uri & scope可加可不加,但加了后必须和配置的match上,否则获取token失败
- 若
AuthorizationServerConfig
配置了public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security.allowFormAuthenticationForClients(); }
,则表示可以使用表单认证,可不用Basic Auth,直接在body中再加入client_id & client_secret
参数
认证服务: 基于JDBC存储数据库方式
数据库
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
/* SQLyog v12.2.6 (64 bit) MySQL - 8.0.15 : Database - dear_v1_auth ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; CREATE DATABASE /*!32312 IF NOT EXISTS*/`dear_v1_auth` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `dear_v1_auth`; /*Table structure for table `clientdetails` */ DROP TABLE IF EXISTS `clientdetails`; CREATE TABLE `clientdetails` ( `appId` varchar(128) NOT NULL, `resourceIds` varchar(256) DEFAULT NULL, `appSecret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `grantTypes` varchar(256) DEFAULT NULL, `redirectUrl` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additionalInformation` varchar(4096) DEFAULT NULL, `autoApproveScopes` varchar(256) DEFAULT NULL, PRIMARY KEY (`appId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Table structure for table `oauth_access_token` */ DROP TABLE IF EXISTS `oauth_access_token`; CREATE TABLE `oauth_access_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar(128) NOT NULL, `user_name` varchar(256) DEFAULT NULL, `client_id` varchar(256) DEFAULT NULL, `authentication` blob, `refresh_token` varchar(256) DEFAULT NULL, PRIMARY KEY (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Table structure for table `oauth_approvals` */ DROP TABLE IF EXISTS `oauth_approvals`; CREATE TABLE `oauth_approvals` ( `userId` varchar(256) DEFAULT NULL, `clientId` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `status` varchar(10) DEFAULT NULL, `expiresAt` timestamp NULL DEFAULT NULL, `lastModifiedAt` timestamp NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Table structure for table `oauth_client_details` */ DROP TABLE IF EXISTS `oauth_client_details`; CREATE TABLE `oauth_client_details` ( `client_id` varchar(128) NOT NULL, `resource_ids` varchar(256) DEFAULT NULL, `client_secret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `authorized_grant_types` varchar(256) DEFAULT NULL, `web_server_redirect_uri` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additional_information` varchar(4096) DEFAULT NULL, `autoapprove` varchar(256) DEFAULT NULL, PRIMARY KEY (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Table structure for table `oauth_client_token` */ DROP TABLE IF EXISTS `oauth_client_token`; CREATE TABLE `oauth_client_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar(128) NOT NULL, `user_name` varchar(256) DEFAULT NULL, `client_id` varchar(256) DEFAULT NULL, PRIMARY KEY (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Table structure for table `oauth_code` */ DROP TABLE IF EXISTS `oauth_code`; CREATE TABLE `oauth_code` ( `code` varchar(256) DEFAULT NULL, `authentication` blob ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Table structure for table `oauth_refresh_token` */ DROP TABLE IF EXISTS `oauth_refresh_token`; CREATE TABLE `oauth_refresh_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication` blob ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
插入client数据 table:
oauth_client_details
- client_id: DearApp
- client_secret: $2a$10$i5enbghKJ1yqj9r7XuyJC.Rs0gGSTzjiv5vQvz/ShghJhjomHaqUa (DearApp加密和的字符串)
- scope: app
- authorized_grant_types: authorization_code,password,refresh_token
- web_server_redirect_uri: http://www.baidu.com
配置数据源
pom.xml
<!-- druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <!-- for @ConfigurationProperties : optional ! --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
- application.yml
spring: datasource: druid: url: jdbc:mysql://localhost:3306/dear_v1_auth?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowMultiQueries=true username: cj password: 123 driver-class-name: com.mysql.cj.jdbc.Driver # type: com.alibaba.druid.pool.DruidDataSource initial-size: 8 min-idle: 1 max-active: 20 max-wait: 60000 time-between-eviction-runsMillis: 60000 min-evictable-idle-timeMillis: 300000 validation-query: select 'x' test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: false max-open-prepared-statements: 20 max-pool-prepared-statement-per-connection-size: 20 filters: stat,wall use-global-data-source-stat: true connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
config/DruidConfig
import com.alibaba.druid.pool.DruidDataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration public class DruidConfig { //https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter @ConfigurationProperties(prefix = "spring.datasource.druid") @Bean public DataSource druidDataSource(){ return new DruidDataSource(); } }
配置授权认证服务器: client & token 存储到数据库
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{ // 1. 配置 client @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(jdbcClientDetailsService()); } @Autowired DataSource dataSource; @Bean public ClientDetailsService jdbcClientDetailsService(){ return new JdbcClientDetailsService(dataSource); } // 2. 配置 endpoints,token存储 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jdbcTokenStore()); } @Bean public TokenStore jdbcTokenStore(){ return new JdbcTokenStore(dataSource); } }
RBAC
-
/* SQLyog v12.2.6 (64 bit) MySQL - 8.0.15 : Database - dear_v1_auth ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; CREATE DATABASE /*!32312 IF NOT EXISTS*/`dear_v1_auth` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `dear_v1_auth`; /*Table structure for table `tb_permission` */ DROP TABLE IF EXISTS `tb_permission`; CREATE TABLE `tb_permission` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `parent_id` bigint(20) DEFAULT NULL COMMENT '父权限', `name` varchar(64) NOT NULL COMMENT '权限名称', `enname` varchar(64) NOT NULL COMMENT '权限英文名称', `url` varchar(255) NOT NULL COMMENT '授权路径', `description` varchar(200) DEFAULT NULL COMMENT '备注', `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8 COMMENT='权限表'; /*Data for the table `tb_permission` */ insert into `tb_permission`(`id`,`parent_id`,`name`,`enname`,`url`,`description`,`created`,`updated`) values (37,0,'系统管理','System','/',NULL,'2019-04-04 23:22:54','2019-04-04 23:22:56'), (38,37,'用户管理','SystemUser','/users/',NULL,'2019-04-04 23:25:31','2019-04-04 23:25:33'), (39,38,'查看用户','SystemUserView','/users/view/**',NULL,'2019-04-04 15:30:30','2019-04-04 15:30:43'), (40,38,'新增用户','SystemUserInsert','/users/insert/**',NULL,'2019-04-04 15:30:31','2019-04-04 15:30:44'), (41,38,'编辑用户','SystemUserUpdate','/users/update/**',NULL,'2019-04-04 15:30:32','2019-04-04 15:30:45'), (42,38,'删除用户','SystemUserDelete','/users/delete/**',NULL,'2019-04-04 15:30:48','2019-04-04 15:30:45'), (44,37,'内容管理','SystemContent','/contents/',NULL,'2019-04-06 18:23:58','2019-04-06 18:24:00'), (45,44,'查看内容','SystemContentView','/contents/view/**',NULL,'2019-04-06 23:49:39','2019-04-06 23:49:41'), (46,44,'新增内容','SystemContentInsert','/contents/insert/**',NULL,'2019-04-06 23:51:00','2019-04-06 23:51:02'), (47,44,'编辑内容','SystemContentUpdate','/contents/update/**',NULL,'2019-04-06 23:51:04','2019-04-06 23:51:06'), (48,44,'删除内容','SystemContentDelete','/contents/delete/**',NULL,'2019-04-06 23:51:08','2019-04-06 23:51:10'); /*Table structure for table `tb_role` */ DROP TABLE IF EXISTS `tb_role`; CREATE TABLE `tb_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `parent_id` bigint(20) DEFAULT NULL COMMENT '父角色', `name` varchar(64) NOT NULL COMMENT '角色名称', `enname` varchar(64) NOT NULL COMMENT '角色英文名称', `description` varchar(200) DEFAULT NULL COMMENT '备注', `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT='角色表'; /*Data for the table `tb_role` */ insert into `tb_role`(`id`,`parent_id`,`name`,`enname`,`description`,`created`,`updated`) values (37,0,'超级管理员','admin',NULL,'2019-04-04 23:22:03','2019-04-04 23:22:05'); /*Table structure for table `tb_role_permission` */ DROP TABLE IF EXISTS `tb_role_permission`; CREATE TABLE `tb_role_permission` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `role_id` bigint(20) NOT NULL COMMENT '角色 ID', `permission_id` bigint(20) NOT NULL COMMENT '权限 ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8 COMMENT='角色权限表'; /*Data for the table `tb_role_permission` */ insert into `tb_role_permission`(`id`,`role_id`,`permission_id`) values (37,37,37), (38,37,38), (39,37,39), (40,37,40), (41,37,41), (42,37,42), (43,37,44), (44,37,45), (45,37,46), (46,37,47), (47,37,48); /*Table structure for table `tb_user` */ DROP TABLE IF EXISTS `tb_user`; CREATE TABLE `tb_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(64) NOT NULL COMMENT '密码,加密存储', `phone` varchar(20) DEFAULT NULL COMMENT '注册手机号', `email` varchar(50) DEFAULT NULL COMMENT '注册邮箱', `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) USING BTREE, UNIQUE KEY `phone` (`phone`) USING BTREE, UNIQUE KEY `email` (`email`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT='用户表'; /*Data for the table `tb_user` */ insert into `tb_user`(`id`,`username`,`password`,`phone`,`email`,`created`,`updated`) values (37,'admin','$2a$10$9ZhDOBp.sRKat4l14ygu/.LscxrMUcDAfeVOEPiYwbcRkoB09gCmi','15888888888','lee.lusifer@gmail.com','2019-04-04 23:21:27','2019-04-04 23:21:29'); /*Table structure for table `tb_user_role` */ DROP TABLE IF EXISTS `tb_user_role`; CREATE TABLE `tb_user_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) NOT NULL COMMENT '用户 ID', `role_id` bigint(20) NOT NULL COMMENT '角色 ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT='用户角色表'; /*Data for the table `tb_user_role` */ insert into `tb_user_role`(`id`,`user_id`,`role_id`) values (37,37,37); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
配置MyBatis: application.yml,main上加
@MapperScan("com.cj.dear.auth.mapper")
注解mybatis: mapper-locations: classpath:mapper/*Mapper.xml config-location: classpath:mybatis-config.xml
编写entity,mapper,service
config/WebSecurityConfig.java
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true) class WebSecurityConfig extends WebSecurityConfigurerAdapter { // 采用bcrypt对密码进行编码 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Autowired UserDetailsServiceImpl userDetailsServiceImpl; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsServiceImpl); } }
UserDetailsServiceImpl.java
@Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private TbUserService tbUserService; @Autowired private TbPermissionService tbPermissionService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { TbUser tbUser = tbUserService.getByUsername(username); if(tbUser!=null){ List<GrantedAuthority> grantedAuthorityList = new ArrayList<>(); List<TbPermission> tbPermissions = tbPermissionService.listPermisionsByUserId(tbUser.getId()); if(tbPermissions!=null){ tbPermissions.forEach(tbPermission -> { grantedAuthorityList.add(new SimpleGrantedAuthority(tbPermission.getEnname())); }); } return new User(tbUser.getUsername(),tbUser.getPassword(),grantedAuthorityList); } return null; } }
资源服务器
pom.yml
<!-- OAuth --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
application.yml
security: oauth2: client: client-id: DearApp client-secret: DearApp access-token-uri: http://localhost:5000/dear-auth/oauth/token user-authorization-uri: http://localhost:5000/dear-auth/authorize resource: token-info-uri: http://localhost:5000/dear-auth/oauth/check_token
config/ResourceServerConfig
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//激活方法上的PreAuthorize注解 public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/").hasAuthority("SystemProject") .antMatchers("/projects/view/**").hasAuthority("SystemProjectView") ; } }
ProjectController
@RestController public class ProjectController { @GetMapping("/") public Object index(){ return "Project Hello World"; } @GetMapping("/projects/view") public Object listProjects(){ return "List Projects"; } @GetMapping("/projects/insert") public Object insertProject(){ return "Insert Projects"; } }
visit
http://localhost:5001/dear-project/?access_token=1e9aedd1-4e92-4444-96a7-edc3b55a3d25
=> Exception (springframework.web.client.HttpClientErrorException$Forbidden: 403 : [{"timestamp":"2021-01-22T15:01:31.342+00:00","status":403,"error":"Forbidden","message":"","path":"/dear-auth/oauth/check_token"}]
)- 方式一: dear-auth项目 config/WebSecurityConfig => visit
http://localhost:5000/dear-auth/oauth/check_token?token=1e9aedd1-4e92-4444-96a7-edc3b55a3d25
成功/// Sample 3: /oauth/check_token 401/403 配置这个或者在AuthorizationServerConfig中配置security.checkTokenAccess策略 @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/oauth/check_token"); }
- 放式二: dear-auth项目 config/AuthorizationServerConfig => visit
http://localhost:5000/dear-auth/oauth/check_token?token=1e9aedd1-4e92-4444-96a7-edc3b55a3d25
+ Basic Auth 成功/// Sample 3: 此认证服务器的安全策略 @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("permitAll()") // /oauth/token_key 完全开放 .checkTokenAccess("isAuthenticated()") // /oauth/check_token 需要认证通过,可采用http basic认证 .allowFormAuthenticationForClients() // 允许表单认证 ; }
- 方式一: dear-auth项目 config/WebSecurityConfig => visit
test
- visit
http://localhost:5000/dear-auth/oauth/authorize?client_id=DearApp&response_type=code
=> 获取code:nfGdkg - POST
http://localhost:5000/dear-auth/oauth/token
=> 获取access_token: 1e9aedd1-4e92-4444-96a7-edc3b55a3d25 - visit
http://localhost:5001/dear-project/
=> unauthorized - visit
http://localhost:5001/dear-project/?access_token=1e9aedd1-4e92-4444-96a7-edc3b55a3d25
=> Success! - visit
http://localhost:5001/dear-project/
+ Bearer Token => Success! - visit
http://localhost:5000/dear-auth/oauth/check_token?token=1e9aedd1-4e92-4444-96a7-edc3b55a3d25
=>{ "active": true, "exp": 1611353145, "user_name": "admin", "authorities": [ "SystemProjectView", "SystemProject", "SystemUserView", "SystemProjectInsert", "SystemProjectUpdate", "SystemUser", "SystemUserInsert", "SystemUserDelete", "SystemUserUpdate", "System", "SystemProjectDelete" ], "client_id": "DearApp", "scope": [ "app" ] }
- visit
Test
POST http://localhost:5000/dear-auth/oauth/token
curl -d 'grant_type=authorization_code&code=3Tgl8q' -X POST http://DearApp:DearApp@localhost:5000/dear-auth/oauth/token -i
GET http://localhost:5001/dear-project/?access_token=1e9aedd1-4e92-4444-96a7-edc3b55a3d25
ISSUE
SpringSecurityOAuth2登录后无法跳转获取授权码地址,直接跳转根路径原因详解 https://blog.csdn.net/CSDN877425287/article/details/110948221
SpringBoot集成SpringSecurity - 异常处理(三) https://www.jianshu.com/p/5b412418b864/
SpringBoot2.x统一异常捕获@RestControllerAdvice https://blog.csdn.net/fuu123f/article/details/107249708
Spring Security OAuth2 授权失败(401) 问题整理 https://www.cnblogs.com/mxmbk/p/9782409.html
SpringBoot /error Error Page status 为 999 的问题 https://learnku.com/java/t/39683
Spring Cloud OAuth2 实现用户认证及单点登录 https://www.cnblogs.com/fengzheng/p/11724625.html
官方 spring-security-oauth2 https://github.com/spring-projects/spring-security-oauth/tree/master/spring-security-oauth2