当前位置:   article > 正文

Spring cloud oauth2搭建OAuth2.0授权服务_springcloud ouath2 allowformauthenticationforclien

springcloud ouath2 allowformauthenticationforclients

我理解的OAuth2.0

首先这是一种授权协议,个人理解这个协议最大的好处是可以使用第三方client进行登录,比如我们在登录csdn博客的时候,可以不必使用用户名密码,直接微信或者qq扫码登录即可。相比与普通的用户名密码登录授权,OAuth2.0多了一个client的概念,client即是你开发的网站,比如要接入微信的登录,你需要在微信网站应用开发创建审核相应的材料,然后获取APPId,此APPID即是client id,通过此client id,即可以使用微信的授权服务,获取token,进行网站的访问。

此处完全是我自己对于OAuth2的理解,如果不对之处,欢迎大佬批评指正。

OAuth2.0的四种授权模式

1. 授权码许可类型(Authorization Code)

2.隐式许可类型(Implicit)

3.客户端凭据许可(Client Credentials)

4.资源拥有者凭据许可(Resource Owner Password Credentials)

在这里我只说一下最复杂的授权码许可类型,其他的三种请参考OAuth 2.0 的四种方式

授权码许可类型是最复杂的授权模式,其流程如下所示。登录客户端需要先获得授权码(authorization_code),然后利用code换取access_token。

代码讲解

总体概述

代码讲解分为两个部分,一个是授权服务,一个是资源服务,资源服务代码主要为了验证生成的access_token能访问相应的资源。

 整个项目的pom文件如下。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns="http://maven.apache.org/POM/4.0.0"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.hz.oauth</groupId>
  7. <artifactId>authority-management-sys-2.0</artifactId>
  8. <version>1.0</version>
  9. <parent>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-parent</artifactId>
  12. <version>2.2.1.RELEASE</version>
  13. </parent>
  14. <modules>
  15. <module>oauth2-resource</module>
  16. <module>oauth2-server</module>
  17. </modules>
  18. <packaging>pom</packaging>
  19. <properties>
  20. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  21. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  22. <java.version>1.8</java.version>
  23. <org.apache.common.lang3.version>3.12.0</org.apache.common.lang3.version>
  24. <spring-framework.cloud.version>Greenwich.SR4</spring-framework.cloud.version>
  25. <org.apache.common.beanutils.version>1.9.4</org.apache.common.beanutils.version>
  26. <com.baomidou.mybatis-plus.version>3.4.3</com.baomidou.mybatis-plus.version>
  27. <mysql.connector.java.version>8.0.12</mysql.connector.java.version>
  28. <com.alibaba.fastjson.version>1.2.76</com.alibaba.fastjson.version>
  29. <!--Lombok-->
  30. <lombok.version>1.18.10</lombok.version>
  31. <commons-io.version>2.6</commons-io.version>
  32. <javadoc.version>3.0.0</javadoc.version>
  33. <maven-release-plugin.version>2.5.3</maven-release-plugin.version>
  34. </properties>
  35. <dependencyManagement>
  36. <dependencies>
  37. <dependency>
  38. <groupId>org.springframework.cloud</groupId>
  39. <artifactId>spring-cloud-dependencies</artifactId>
  40. <version>${spring-framework.cloud.version}</version>
  41. <type>pom</type>
  42. <scope>import</scope>
  43. </dependency>
  44. <dependency>
  45. <groupId>org.projectlombok</groupId>
  46. <artifactId>lombok</artifactId>
  47. <version>${lombok.version}</version>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.apache.commons</groupId>
  51. <artifactId>commons-lang3</artifactId>
  52. <version>${org.apache.common.lang3.version}</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>commons-beanutils</groupId>
  56. <artifactId>commons-beanutils</artifactId>
  57. <version>${org.apache.common.beanutils.version}</version>
  58. </dependency>
  59. <dependency>
  60. <groupId>com.baomidou</groupId>
  61. <artifactId>mybatis-plus-boot-starter</artifactId>
  62. <version>${com.baomidou.mybatis-plus.version}</version>
  63. </dependency>
  64. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  65. <dependency>
  66. <groupId>mysql</groupId>
  67. <artifactId>mysql-connector-java</artifactId>
  68. <version>${mysql.connector.java.version}</version>
  69. </dependency>
  70. <dependency>
  71. <groupId>com.alibaba</groupId>
  72. <artifactId>fastjson</artifactId>
  73. <version>${com.alibaba.fastjson.version}</version>
  74. </dependency>
  75. </dependencies>
  76. </dependencyManagement>
  77. <build>
  78. <plugins>
  79. <!-- for create custom archetype template -->
  80. <plugin>
  81. <groupId>org.apache.maven.plugins</groupId>
  82. <artifactId>maven-archetype-plugin</artifactId>
  83. <version>3.0.1</version>
  84. </plugin>
  85. <plugin>
  86. <groupId>org.apache.maven.plugins</groupId>
  87. <artifactId>maven-release-plugin</artifactId>
  88. <version>${maven-release-plugin.version}</version>
  89. </plugin>
  90. </plugins>
  91. </build>
  92. </project>

oauth2-server

最为重要的是两个配置文件类,一个是授权服务的配置,一个是security的安全服务配置,security的安全服务配置重要的是把授权服务相应的地址配置允许匿名访问。

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns="http://maven.apache.org/POM/4.0.0"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <groupId>com.hz.oauth</groupId>
  7. <artifactId>authority-management-sys-2.0</artifactId>
  8. <version>1.0</version>
  9. </parent>
  10. <artifactId>oauth2-server</artifactId>
  11. <modelVersion>4.0.0</modelVersion>
  12. <dependencies>
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-web</artifactId>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework.cloud</groupId>
  19. <artifactId>spring-cloud-starter-oauth2</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.projectlombok</groupId>
  23. <artifactId>lombok</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.apache.commons</groupId>
  27. <artifactId>commons-lang3</artifactId>
  28. </dependency>
  29. <dependency>
  30. <groupId>commons-beanutils</groupId>
  31. <artifactId>commons-beanutils</artifactId>
  32. </dependency>
  33. <dependency>
  34. <groupId>mysql</groupId>
  35. <artifactId>mysql-connector-java</artifactId>
  36. </dependency>
  37. <dependency>
  38. <groupId>com.baomidou</groupId>
  39. <artifactId>mybatis-plus-boot-starter</artifactId>
  40. </dependency>
  41. <dependency>
  42. <groupId>com.alibaba</groupId>
  43. <artifactId>fastjson</artifactId>
  44. </dependency>
  45. <dependency>
  46. <groupId>org.springframework.boot</groupId>
  47. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  48. </dependency>
  49. </dependencies>
  50. <build>
  51. <plugins>
  52. <!-- for create custom archetype template -->
  53. <plugin>
  54. <groupId>org.apache.maven.plugins</groupId>
  55. <artifactId>maven-archetype-plugin</artifactId>
  56. <version>3.0.1</version>
  57. </plugin>
  58. <plugin>
  59. <groupId>org.apache.maven.plugins</groupId>
  60. <artifactId>maven-release-plugin</artifactId>
  61. <version>${maven-release-plugin.version}</version>
  62. </plugin>
  63. <plugin>
  64. <groupId>org.sonarsource.scanner.maven</groupId>
  65. <artifactId>sonar-maven-plugin</artifactId>
  66. <version>3.0.2</version>
  67. </plugin>
  68. <plugin>
  69. <groupId>org.apache.maven.plugins</groupId>
  70. <artifactId>maven-compiler-plugin</artifactId>
  71. <configuration>
  72. <source>1.8</source>
  73. <target>1.8</target>
  74. </configuration>
  75. </plugin>
  76. </plugins>
  77. <resources>
  78. <resource>
  79. <directory>src/main/java</directory>
  80. <includes>
  81. <include>**/*.xml</include>
  82. </includes>
  83. </resource>
  84. <resource>
  85. <directory>src/main/webapp</directory>
  86. <targetPath>META-INF/resources</targetPath>
  87. <includes>
  88. <include>**/**</include>
  89. </includes>
  90. </resource>
  91. <resource>
  92. <directory>src/main/resources</directory>
  93. <includes>
  94. <include>**/**</include>
  95. </includes>
  96. </resource>
  97. <resource>
  98. <directory>src/main/resources</directory>
  99. <filtering>true</filtering>
  100. <excludes>
  101. <exclude>**/*.jks</exclude>
  102. </excludes>
  103. </resource>
  104. <resource>
  105. <directory>src/main/resources</directory>
  106. <filtering>false</filtering>
  107. <includes>
  108. <include>**/*.jks</include>
  109. </includes>
  110. </resource>
  111. </resources>
  112. </build>
  113. </project>

SysAuthorizationConfig

在该类配置中,授权码的保存,用户的授权记录保存在数据库中,其生成数据库代码为

  1. DROP TABLE IF EXISTS `oauth_approvals`;
  2. CREATE TABLE `oauth_approvals` (
  3. `userId` varchar(256) DEFAULT NULL,
  4. `clientId` varchar(256) DEFAULT NULL,
  5. `partnerKey` varchar(32) DEFAULT NULL,
  6. `scope` varchar(256) DEFAULT NULL,
  7. `status` varchar(10) DEFAULT NULL,
  8. `expiresAt` datetime DEFAULT NULL,
  9. `lastModifiedAt` datetime DEFAULT NULL
  10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
  1. DROP TABLE IF EXISTS `oauth_code`;
  2. CREATE TABLE `oauth_code` (
  3. `code` varchar(255) DEFAULT NULL,
  4. `authentication` blob
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

SysAuthorizationConfig类配置代码

  1. /**
  2. * @program: OAuth-2.0
  3. * @author: zgr
  4. * @create: 2021-07-20 11:42
  5. **/
  6. @Configuration
  7. @EnableAuthorizationServer
  8. public class SysAuthorizationConfig extends AuthorizationServerConfigurerAdapter {
  9. private final DataSource dataSource;
  10. private final SysClientService sysClientService;
  11. private final AuthenticationManager authenticationManager;
  12. public SysAuthorizationConfig(DataSource dataSource, SysClientService sysClientService, AuthenticationManager authenticationManager) {
  13. this.dataSource = dataSource;
  14. this.sysClientService = sysClientService;
  15. this.authenticationManager = authenticationManager;
  16. }
  17. /**
  18. * 维护一套客户端的信息 即第三方应用软件申请时记录的一些基本信息
  19. *
  20. * @param clients
  21. * @throws Exception
  22. */
  23. @Override
  24. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  25. clients.withClientDetails(sysClientService);
  26. }
  27. /**
  28. * 打开验证token的访问权限
  29. *
  30. * @param security
  31. */
  32. @Override
  33. public void configure(AuthorizationServerSecurityConfigurer security) {
  34. security
  35. .allowFormAuthenticationForClients()
  36. .tokenKeyAccess("isAuthenticated()")
  37. .checkTokenAccess("permitAll()")
  38. .passwordEncoder(new BCryptPasswordEncoder());
  39. }
  40. /**
  41. * 配置了令牌存储方式为jwt
  42. * 配置JWT Token的非对称加密来进行签名
  43. * 配置一个自定义的Token增强器,把更多信息放入Token中
  44. * 配置使用JDBC数据库方式来保存用户的授权批准记录
  45. *
  46. * @param endpoints
  47. */
  48. @Override
  49. public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
  50. TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
  51. tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtTokenEnhancer()));
  52. endpoints.approvalStore(approvalStore())
  53. .authorizationCodeServices(authorizationCodeServices())
  54. .tokenEnhancer(tokenEnhancerChain)
  55. .tokenStore(tokenStore())
  56. .authenticationManager(authenticationManager);
  57. }
  58. /**
  59. * 使用数据库方式来保存授权码
  60. *
  61. * @return
  62. */
  63. @Bean
  64. public AuthorizationCodeServices authorizationCodeServices() {
  65. return new JdbcAuthorizationCodeServices(dataSource);
  66. }
  67. /**
  68. * 采用jwt方式存储token
  69. *
  70. * @return
  71. */
  72. @Bean
  73. public TokenStore tokenStore() {
  74. return new JwtTokenStore(jwtTokenEnhancer());
  75. }
  76. /**
  77. * 使用数据库方式存储用户的批准授权记录
  78. *
  79. * @return
  80. */
  81. @Bean
  82. public JdbcApprovalStore approvalStore() {
  83. return new JdbcApprovalStore(dataSource);
  84. }
  85. /**
  86. * 自定义token增强器
  87. *
  88. * @return
  89. */
  90. @Bean
  91. public TokenEnhancer tokenEnhancer() {
  92. return new SysTokenEnhancer();
  93. }
  94. /**
  95. * jwt 验证
  96. *
  97. * @return
  98. */
  99. @Bean
  100. protected JwtAccessTokenConverter jwtTokenEnhancer() {
  101. JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
  102. // 设置jwt加解密秘钥,不设置会随机一个
  103. jwtAccessTokenConverter.setSigningKey(Constant.JWT_SECRET);
  104. return jwtAccessTokenConverter;
  105. }
  106. }

SysSecurityConfig

  1. **
  2. * @program: authority-management-sys-2.0
  3. * @author: zgr
  4. * @create: 2021-08-03 09:55
  5. **/
  6. @Configuration
  7. @EnableWebSecurity
  8. public class SysSecurityConfig extends WebSecurityConfigurerAdapter {
  9. private final SysUserService sysUserService;
  10. private final SysLogoutHandler sysLogoutHandler;
  11. private final SysLogoutSuccessHandler sysLogoutSuccessHandler;
  12. public SysSecurityConfig(SysUserService sysUserService, SysLogoutHandler sysLogoutHandler, SysLogoutSuccessHandler sysLogoutSuccessHandler) {
  13. this.sysUserService = sysUserService;
  14. this.sysLogoutHandler = sysLogoutHandler;
  15. this.sysLogoutSuccessHandler = sysLogoutSuccessHandler;
  16. }
  17. /**
  18. * 配置认证管理器
  19. *
  20. * @return
  21. * @throws Exception
  22. */
  23. @Bean
  24. @Override
  25. public AuthenticationManager authenticationManagerBean() throws Exception {
  26. return super.authenticationManagerBean();
  27. }
  28. /**
  29. * 这里是对认证管理器的添加配置,自定义用户详情
  30. *
  31. * @param auth
  32. * @throws Exception
  33. */
  34. @Override
  35. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  36. auth.userDetailsService(sysUserService).passwordEncoder(new BCryptPasswordEncoder())
  37. ;
  38. }
  39. //web ignore比较适合配置前端相关的静态资源,它是完全绕过spring security的所有filter的
  40. @Override
  41. public void configure(WebSecurity web) throws Exception {
  42. web.ignoring().antMatchers("/resource");
  43. }
  44. /**
  45. * 安全请求配置,这里配置的是security的部分,这里配置全部通过,安全拦截在资源服务的配置文件中配置,
  46. * 要不然访问未验证的接口将重定向到登录页面,前后端分离的情况下这样并不友好,无权访问接口返回相关错误信息即可
  47. *
  48. * @param http
  49. * @return void
  50. */
  51. @Override
  52. protected void configure(HttpSecurity http) throws Exception {
  53. http
  54. .formLogin().loginPage("/login").and()
  55. .logout().addLogoutHandler(sysLogoutHandler).logoutSuccessHandler(sysLogoutSuccessHandler)
  56. .and()
  57. // 由于使用的是JWT,我们这里不需要csrf
  58. .csrf().disable().cors().and()
  59. .authorizeRequests()
  60. .antMatchers("/login", "/oauth/authorize").permitAll()
  61. // 除上面外的所有请求全部需要鉴权认证
  62. .anyRequest().authenticated();
  63. }
  64. }

客户端服务ClientDetailsService

在SysAuthorizationConfig中,我们配置的是客户端 服务为自定义的sysClientService。实际应用中,通过client id查出对应客户端的信息,查询其拥有的resource资源权限,生成access_token。

  1. /**
  2. * 维护一套客户端的信息 即第三方应用软件申请时记录的一些基本信息
  3. *
  4. * @param clients
  5. * @throws Exception
  6. */
  7. @Override
  8. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  9. clients.withClientDetails(sysClientService);
  10. }
  1. @Data
  2. @Builder
  3. @AllArgsConstructor
  4. @NoArgsConstructor
  5. public class SysClientDetails implements ClientDetails {
  6. private String clientId;
  7. private Set<String> resourceId;
  8. private Boolean isSecretRequired;
  9. private String clientSecret;
  10. private Boolean isScoped;
  11. private Set<String> scope;
  12. private Set<String> authorizedGrantTypes;
  13. private Set<String> registeredRedirectUri;
  14. private Integer accessTokenValiditySeconds;
  15. private Integer refreshTokenValiditySeconds;
  16. private Boolean isAutoApprove;
  17. private Map<String, Object> additionalInformation;
  18. @Override
  19. public String getClientId() {
  20. return this.clientId;
  21. }
  22. @Override
  23. public Set<String> getResourceIds() {
  24. return this.resourceId;
  25. }
  26. @Override
  27. public boolean isSecretRequired() {
  28. return this.isSecretRequired;
  29. }
  30. @Override
  31. public String getClientSecret() {
  32. return this.clientSecret;
  33. }
  34. @Override
  35. public boolean isScoped() {
  36. return this.isScoped;
  37. }
  38. @Override
  39. public Set<String> getScope() {
  40. return this.scope;
  41. }
  42. @Override
  43. public Set<String> getAuthorizedGrantTypes() {
  44. return this.authorizedGrantTypes;
  45. }
  46. @Override
  47. public Set<String> getRegisteredRedirectUri() {
  48. return this.registeredRedirectUri;
  49. }
  50. @Override
  51. public Collection<GrantedAuthority> getAuthorities() {
  52. return Collections.emptyList();
  53. }
  54. @Override
  55. public Integer getAccessTokenValiditySeconds() {
  56. return this.accessTokenValiditySeconds;
  57. }
  58. @Override
  59. public Integer getRefreshTokenValiditySeconds() {
  60. return this.refreshTokenValiditySeconds;
  61. }
  62. @Override
  63. public boolean isAutoApprove(String scope) {
  64. return this.isAutoApprove;
  65. }
  66. @Override
  67. public Map<String, Object> getAdditionalInformation() {
  68. return this.additionalInformation;
  69. }
  70. }
  1. /**
  2. * @program: authority-management-sys-2.0
  3. * @author: zgr
  4. * @create: 2021-08-03 10:12
  5. **/
  6. @Service
  7. @Slf4j
  8. public class SysClientServiceImpl implements SysClientService {
  9. @Override
  10. public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
  11. log.info("查找客户端id为 {} 的客户端详情", clientId);
  12. //实际使用中可以从数据库查询客户端详细信息,本例子中直接构造一个客户端
  13. Set<String> resourceIds = new HashSet<>();
  14. resourceIds.add("userservice1");
  15. Set<String> scopes = new HashSet<>(1);
  16. scopes.add("sever");
  17. Set<String> grantTypes = new HashSet<>(5);
  18. grantTypes.add("password");
  19. grantTypes.add("authorization_code");
  20. Set<String> registeredRedirectUris = new HashSet<>(1);
  21. registeredRedirectUris.add("https://baidu.com");
  22. return SysClientDetails.builder()
  23. //客户端id
  24. .clientId("userservice1")
  25. //拥有的资源id
  26. .resourceId(resourceIds)
  27. //是否需要secret
  28. .isSecretRequired(true)
  29. //secret加密方式
  30. .clientSecret(new BCryptPasswordEncoder().encode("1234"))
  31. //权限范围
  32. .scope(scopes)
  33. //支持的授权模式,四种模式支持哪几种
  34. .authorizedGrantTypes(grantTypes)
  35. //官方注册的回调地址,这个地址是需要和授权请求的回调地址一致
  36. .registeredRedirectUri(registeredRedirectUris)
  37. .isScoped(true)
  38. //access_token的有效时间
  39. .accessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(2))
  40. //refresh_token的有效时间
  41. .refreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30))
  42. //是否可以自主授权
  43. .isAutoApprove(true)
  44. .build();
  45. }
  46. }

postman演示结果

1.资源有着凭据许可

 完整的返回数据:

  1. {
  2. "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiZXhwIjoxNjI4NzU3NTMzLCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiODAwZjgwMmUtZTUzNS00N2RiLTkzOTUtZjY5ZmQxM2Q3ZjNjIiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.N8HLGnDbRiN560SuE_a4IlqEZIk3yCpzpyEXH3VDb1w",
  3. "token_type": "bearer",
  4. "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiYXRpIjoiODAwZjgwMmUtZTUzNS00N2RiLTkzOTUtZjY5ZmQxM2Q3ZjNjIiwiZXhwIjoxNjMxMTc2NzMyLCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiZDMyNGQ4ZjEtMjBiMy00MTg2LTk4NzMtZjg2NDhkNjI1ZTc5IiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.KbqODRoK0hjrrHWeKvtZ2HNkcREPFmuv7yvOrZGzEnE",
  5. "expires_in": 172799,
  6. "scope": "sever",
  7. "userDetails": {
  8. "id": 1,
  9. "username": "admin",
  10. "password": null,
  11. "roles": [
  12. {
  13. "id": 1,
  14. "name": "READ"
  15. },
  16. {
  17. "id": 1,
  18. "name": "WRITE"
  19. }
  20. ],
  21. "authorities": [
  22. {
  23. "authority": "READ"
  24. },
  25. {
  26. "authority": "WRITE"
  27. }
  28. ],
  29. "enabled": true,
  30. "credentialsNonExpired": true,
  31. "accountNonExpired": true,
  32. "accountNonLocked": true
  33. },
  34. "jti": "800f802e-e535-47db-9395-f69fd13d7f3c"
  35. }

 2.授权码模式

在浏览器输入地址http://localhost:8086/api/v1/oauth/authorize?response_type=code&client_id=userservice1&redirect_uri=https://baidu.com,会跳转到授权页面,授权页面利用spring mvc定义了一个页面放在resources目录下。

  1. <!DOCTYPE html>
  2. <html class="uk-height-1-1" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8"/>
  5. <title>OAuth2 Demo</title>
  6. <link href="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.26.3/css/uikit.gradient.min.css" rel="stylesheet"/>
  7. </head>
  8. <body class="uk-height-1-1">
  9. <div class="uk-vertical-align uk-text-center uk-height-1-1">
  10. <div class="uk-vertical-align-middle" style="width: 250px;">
  11. <h1>Login Form</h1>
  12. <p class="uk-text-danger" th:if="${param.error}">
  13. 用户名或密码错误...
  14. </p>
  15. <form class="uk-panel uk-panel-box uk-form" method="post" th:action="@{/login}">
  16. <div class="uk-form-row">
  17. <input class="uk-width-1-1 uk-form-large" name="username" placeholder="Username" type="text"
  18. value="admin"/>
  19. </div>
  20. <div class="uk-form-row">
  21. <input class="uk-width-1-1 uk-form-large" name="password" placeholder="Password" type="password"
  22. value="admin"/>
  23. </div>
  24. <div class="uk-form-row">
  25. <button class="uk-width-1-1 uk-button uk-button-primary uk-button-large">Login</button>
  26. </div>
  27. </form>
  28. </div>
  29. </div>
  30. </body>
  31. </html>

 跳转到登录页面,点击登录,登录成功后,跳转到百度页面,并得到code授权码

 

 拿到获取的code,生成access_token

完整的返回信息

  1. {
  2. "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiZXhwIjoxNjI4NzU4Mjc2LCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiZGM5Y2FjN2UtN2M3My00YThkLWEzMzYtZTI2ZmNjYzllODE0IiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.sOhH-eFcldWnnOfZmDqyuPkl4Qkg8LyJnPZ0gwubBWc",
  3. "token_type": "bearer",
  4. "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiYXRpIjoiZGM5Y2FjN2UtN2M3My00YThkLWEzMzYtZTI2ZmNjYzllODE0IiwiZXhwIjoxNjMxMTc3NDc2LCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiNDdmYjUzMDctYzcyYy00ZjI1LWI4MjEtNTY2MGNlYWYyZGMwIiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.vmtnmLWsaGBxFxAZ-0nsETBUsZ3muSFKqjffAweRZTQ",
  5. "expires_in": 172799,
  6. "scope": "sever",
  7. "userDetails": {
  8. "id": 1,
  9. "username": "admin",
  10. "password": null,
  11. "roles": [
  12. {
  13. "id": 1,
  14. "name": "READ"
  15. },
  16. {
  17. "id": 1,
  18. "name": "WRITE"
  19. }
  20. ],
  21. "authorities": [
  22. {
  23. "authority": "READ"
  24. },
  25. {
  26. "authority": "WRITE"
  27. }
  28. ],
  29. "enabled": true,
  30. "credentialsNonExpired": true,
  31. "accountNonExpired": true,
  32. "accountNonLocked": true
  33. },
  34. "jti": "dc9cac7e-7c73-4a8d-a336-e26fccc9e814"
  35. }

oauth2-resource

oauth2-resource为资源,用户在获得access_token后,携带access_token访问资源。最重要的是资源配置类。

  1. @Configuration
  2. //启用资源服务器
  3. @EnableResourceServer
  4. //启用方法注解方式来进行权限控制
  5. @EnableGlobalMethodSecurity(prePostEnabled = true)
  6. public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
  7. /**
  8. * 声明了资源服务器的ID是userservice,声明了资源服务器的TokenStore是JWT
  9. * 注意,这里硬编码了资源id,这个资源id和client所拥有的的资源id需要匹配
  10. *
  11. * @param resources
  12. * @throws Exception
  13. */
  14. @Override
  15. public void configure(ResourceServerSecurityConfigurer resources) {
  16. resources.resourceId("userservice1").tokenStore(tokenStore());
  17. }
  18. /**
  19. * 配置TokenStore
  20. *
  21. * @return
  22. */
  23. @Bean
  24. public TokenStore tokenStore() {
  25. return new JwtTokenStore(jwtAccessTokenConverter());
  26. }
  27. /**
  28. * 配置解析
  29. *
  30. * @return
  31. */
  32. @Bean
  33. protected JwtAccessTokenConverter jwtAccessTokenConverter() {
  34. JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
  35. //和授权服务的key相同
  36. converter.setSigningKey(Constant.JWT_SECRET);
  37. return converter;
  38. }
  39. }

security配置接口访问权限

  1. @Configuration
  2. @EnableWebSecurity
  3. public class ResourceSecurityConfig extends WebSecurityConfigurerAdapter {
  4. //web ignore比较适合配置前端相关的静态资源,它是完全绕过spring security的所有filter的
  5. @Override
  6. public void configure(WebSecurity web) throws Exception {
  7. web.ignoring().antMatchers("/resource");
  8. }
  9. /**
  10. * 安全请求配置,这里配置的是security的部分,这里配置全部通过,安全拦截在资源服务的配置文件中配置,
  11. * 要不然访问未验证的接口将重定向到登录页面,前后端分离的情况下这样并不友好,无权访问接口返回相关错误信息即可
  12. *
  13. * @param http
  14. * @return void
  15. */
  16. @Override
  17. protected void configure(HttpSecurity http) throws Exception {
  18. http.authorizeRequests()
  19. .antMatchers("/user/**").authenticated()
  20. .anyRequest().permitAll();
  21. }
  22. }

ctrl接口测试类

  1. **
  2. * @author honorzhang
  3. */
  4. @RestController
  5. @RequestMapping("/user")
  6. public class ResourceUserController {
  7. @Autowired
  8. private TokenStore tokenStore;
  9. /***
  10. * 读权限或写权限可访问,返回登录用户名
  11. * @param authentication
  12. * @return
  13. */
  14. @PreAuthorize("hasAuthority('READ') or hasAuthority('WRITE')")
  15. @GetMapping("/name")
  16. public BaseResponse<String> name(OAuth2Authentication authentication) {
  17. return BaseResponse.success(authentication.getName());
  18. }
  19. /**
  20. * 读权限或写权限可访问,返回登录用户信息
  21. *
  22. * @param authentication
  23. * @return
  24. */
  25. @PreAuthorize("hasAuthority('READ') or hasAuthority('WRITE')")
  26. @GetMapping
  27. public BaseResponse<OAuth2Authentication> read(OAuth2Authentication authentication) {
  28. return BaseResponse.success(authentication);
  29. }
  30. /**
  31. * 只有写权限可以访问,返回访问令牌中的额外信息
  32. *
  33. * @param authentication
  34. * @return
  35. */
  36. @PreAuthorize("hasAuthority('WRITE') and hasAuthority('ADMIN')")
  37. @PostMapping
  38. public BaseResponse<Object> write(OAuth2Authentication authentication) {
  39. OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
  40. OAuth2AccessToken accessToken = tokenStore.readAccessToken(details.getTokenValue());
  41. return BaseResponse.success(accessToken.getAdditionalInformation().getOrDefault("userDetails", null));
  42. }
  43. }

postman结果测试

access_token 权限包含WRITE 和 READ权限,没有ADMIN权限。

访问read接口,权限通过。

 访问write接口,没有ADMIN权限,不能访问

引用

极客时间OAuth2.0课程

写在最后

本文是根据极客时间的课程,利用spring cloud oauth2 实现的OAuth2.0权限控制。有很多是基于自己的理解,如有不正确之处,请互相指教。

代码连接

https://github.com/airhonor/authority-management-sys-2.0

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/505681
推荐阅读
相关标签
  

闽ICP备14008679号