• SpringCloud-OAuth2(二):实战篇


    这篇讲的内容是:Oauth2在SpringBoot/SpringCloud中的实战。

    SpringBoot版本:2.2.5.Release
    SpringCloud版本:Hoxton.SR9
    JDK版本:1.8

    1:POM配置

    1. <dependencies>
    2. <dependency>
    3. <artifactId>spring-cloud-starter-oauth2artifactId>
    4. <groupId>org.springframework.cloudgroupId>
    5. dependency>
    6. <dependency>
    7. <groupId>org.springframework.bootgroupId>
    8. <artifactId>spring-boot-starter-data-redisartifactId>
    9. dependency>
    10. <dependency>
    11. <groupId>org.jasyptgroupId>
    12. <artifactId>jasyptartifactId>
    13. <version>1.9.2version>
    14. dependency>
    15. dependencies>

    2:关键配置

    2.1:认证服务配置-WebAuthorizationConfig

    1. @Configuration
    2. @EnableAuthorizationServer
    3. public class WebAuthorizationConfig extends AuthorizationServerConfigurerAdapter {
    4. private final AuthenticationManager authenticationManager;
    5. private final UserDetailsService userDetailsService;
    6. private final PasswordEncoder passwordEncoder;
    7. private final TokenStore tokenStore;
    8. private final AuthorizationCodeServices authorizationCodeServices;
    9. private final AuthTokenExceptionHandler authTokenExceptionHandler;
    10. public WebAuthorizationConfig(AuthenticationManager authenticationManager,
    11. UserDetailsService userDetailsService,
    12. PasswordEncoder passwordEncoder,
    13. TokenStore tokenStore,
    14. AuthorizationCodeServices authorizationCodeServices,
    15. AuthTokenExceptionHandler authTokenExceptionHandler) {
    16. this.authenticationManager = authenticationManager;
    17. this.userDetailsService = userDetailsService;
    18. this.passwordEncoder = passwordEncoder;
    19. this.tokenStore = tokenStore;
    20. this.authorizationCodeServices = authorizationCodeServices;
    21. this.authTokenExceptionHandler = authTokenExceptionHandler;
    22. }
    23. @Override
    24. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    25. String secret = PasswordHelper.encryptPassword(Oauth2ClientUserEnums.ADMIN.getClientSecret());
    26. clients.inMemory()
    27. .withClient(Oauth2ClientUserEnums.ADMIN.getClientId())
    28. .secret(secret)
    29. .scopes("all", "test")
    30. .resourceIds("admin")
    31. // autoApprove 可跳过授权页直接返回code
    32. .autoApprove("all")
    33. .redirectUris("http://www.baidu.com")
    34. //客户端认证所支持的授权类型 1:客户端凭证 2:账号密码 3:授权码 4:token刷新 5:简易模式
    35. .authorizedGrantTypes(CLIENT_CREDENTIALS, PASSWORD, REFRESH_TOKEN, AUTHORIZATION_CODE, IMPLICIT)
    36. //用户角色
    37. .authorities("admin")
    38. //允许自动授权
    39. .autoApprove(false)
    40. //token 过期时间
    41. .accessTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(12))
    42. //refresh_token 过期时间
    43. .refreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30))
    44. ;
    45. }
    46. @Override
    47. public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    48. security
    49. .passwordEncoder(passwordEncoder) //设置密码编辑器
    50. .allowFormAuthenticationForClients()
    51. .tokenKeyAccess("permitAll()") //开启 /oauth/token_key 的访问权限控制
    52. .checkTokenAccess("permitAll()") //开启 /oauth/check_token 验证端口认证权限访问
    53. ;
    54. }
    55. @Override
    56. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    57. // 配置授权服务器端点的属性
    58. endpoints.authenticationManager(authenticationManager) //认证管理器
    59. .tokenStore(tokenStore)
    60. .authorizationCodeServices(authorizationCodeServices)
    61. .userDetailsService(userDetailsService)
    62. .exceptionTranslator(authTokenExceptionHandler);
    63. }
    64. }
    注解:@EnableAuthorizationServer表明当前服务是认证服务。

    介绍一下几个基础组件


    ①:authenticationManager
    认证管理器,对客户端凭证、用户进行认证的地方。


    ②:tokenStore
    存放token的地方,默认是存放在Inmemory(内存)中的。


    ③:authorizationCodeServices
    code生成服务,使用默认的即可。


    ④:userDetailsService
    用户详情服务,可重写实现,用户信息从数据库中加载。


    ⑤:authTokenExceptionHandler
    自定义的 token 鉴别失败异常处理器。


    ⑥:authClientExceptionHandler
    自定义的 客户端凭证 鉴别失败异常处理器。

    2.2:资源服务配置-WebResourceConfig

    1. @Configuration
    2. @EnableResourceServer
    3. public class WebResourceConfig extends ResourceServerConfigurerAdapter {
    4. private final AuthClientExceptionHandler authClientExceptionHandler;
    5. public WebResourceConfig(AuthClientExceptionHandler authClientExceptionHandler) {
    6. this.authClientExceptionHandler = authClientExceptionHandler;
    7. }
    8. @Override
    9. public void configure(ResourceServerSecurityConfigurer resources) {
    10. resources.resourceId("admin").stateless(true).authenticationEntryPoint(authClientExceptionHandler);
    11. }
    12. @Override
    13. public void configure(HttpSecurity http) throws Exception {
    14. // 资源链路
    15. http
    16. .sessionManagement()
    17. .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    18. .and().formLogin().permitAll()
    19. // 登录放通
    20. .and()
    21. .authorizeRequests()
    22. .antMatchers("/oauth/**", "/favicon.ico")
    23. //.authenticated()
    24. .permitAll()
    25. // 其他请求都需认证
    26. .and()
    27. .authorizeRequests()
    28. .anyRequest()
    29. .authenticated()
    30. // 跨域
    31. .and()
    32. .cors()
    33. // 关闭跨站请求防护
    34. .and()
    35. .csrf()
    36. .disable();
    37. }
    38. }

    注解:@EnableResourceServer表明当前服务是认证服务。

    笔记:
    为什么要放开 /favicon.ico 的访问权限?因为在进行授权码模式的时候,一直无法跳转到我定义的redirect_uri 地址中去。
    使用 logging.level.org.springframework.security=debug 发现会访问 /favicon.ico ,然后报错,再然后就是调到登录页去了。
    因此 /favicon.ico 放开之后,认证正常,成功调到redirect_uri 地址并返回了code。(这是OAuth2的小坑吗?)

    2.3:安全配置-WebSecurityConfig

    1. @Configuration
    2. @EnableWebSecurity
    3. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    4. private final PasswordEncoder passwordEncoder;
    5. public WebSecurityConfig(PasswordEncoder passwordEncoder) {
    6. this.passwordEncoder = passwordEncoder;
    7. }
    8. @Override
    9. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    10. auth
    11. .userDetailsService(userDetailsService())
    12. .passwordEncoder(passwordEncoder); //为认证管理器配置passwordEncoder,无论客户端凭证密码还是用户密码都通过passwordEncoder进行密码匹配
    13. }
    14. @Bean
    15. @Override
    16. protected UserDetailsService userDetailsService() {
    17. return new VipUserDetailService();
    18. }
    19. @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    20. @Override
    21. public AuthenticationManager authenticationManagerBean() throws Exception {
    22. return super.authenticationManagerBean();
    23. }
    24. }

    详细配置可以翻阅我的GitHub:GitHub地址

    3:认证授权演示

    3.1:授权码模式

    注意!!!一定要配置redirect_uri,并且一定要允许表单登录!!!
    以下是步骤:


    ①:浏览器输入以下链接地址,会重定向到登录页
    http://localhost:8123/oauth/authorize?client_id=admin&client_secret=admin&response_type=code&redirect_uri=http://www.baidu.com&scope=test


    ②:输入配置的用户名密码,回调到授权页

    如果我们配置的scope是autoApprove的,即可跳过这步,直接到第③步,拿到code。


    ③:选择 Approve,点击Authorize 即可调到redirect_uri地址


    ④:拿code 换 token

    我的请求地址:127.0.0.1:8123/oauth/token?grant_type=authorization_code&code=h35Fh1&redirect_uri=http://www.baidu.com
    需要配置Authorization,请求时会将客户端凭证以base64格式放在请求头中。

    3.2:用户密码模式

    我的请求地址:127.0.0.1:8123/oauth/token?grant_type=password&username=found&password=123456
    需要配置Authorization,请求时会将客户端凭证以base64格式放在请求头中。

    3.3:客户端凭证模式

    我的请求地址:127.0.0.1:8123/oauth/token?grant_type=client_credentials
    需要配置Authorization,请求时会将客户端凭证以base64格式放在请求头中。

    3.4:隐式授权模式

    浏览器输入以下地址即可拿到token:
    http://localhost:8123/oauth/authorize?client_id=admin&client_secret=admin&response_type=token&redirect_uri=http://www.baidu.com

    3.5:token 刷新

    我的请求地址:127.0.0.1:8123/oauth/token?grant_type=refresh_token&refresh_token=dde5f388-4ad7-4781-a1b7-aaafb3c34b10
    refresh_token是服务器颁发给客户端的,作用就是在一定时间内,让用户不用重新登录。
    需要配置Authorization,请求时会将客户端凭证以base64格式放在请求头中。

    4:聊聊第三方登录

    个人认为做第三方登录是当前比较流行的做法。

    4.1:流程

    ①:比如我们现在登录ProcessOn,就可以选择第三方登录。


    ②:点击QQ登录后会调到这个页面↓↓↓↓↓↓↓↓↓↓↓↓


    ③:上面这个client_id 应该就是ProcessOn 官方申请的第三方客户端凭证了。
    选择账号密码登录,输入账号密码之后就可以拿到token,(我手速快截了个图)


    ④:可以看到返回了access_token、过期时间。
    随后processOn既发起请求进行登录操作,登录成功进入用户首页,失败则重定向到登录页。
     

    4.2:uml图

    可以看到第三方登录期间是不需要手动approve的,因此scope=all是autoApprove的。
    认真辨别了一下,QQ第三方登录采用不是授权码模式,而是Implicat Grant Type。(差点看走眼)

    总结 :
    学习总要从身边找例子,这样才能更好的理解。

  • 相关阅读:
    【OpenCV 例程200篇】210. 绘制直线也会有这么多坑?
    MYSQL一站式学习,看完即学完
    赛码系统——根据文件生成时间先后顺序对文件进行排序
    windows查看每个程序建立的TCP链接数量
    Leetcode1-两数之和
    网络是怎样连接的--DNS服务器查询原理
    认识一下MRS里的“中间人”Alluxio
    电子学会青少年软件编程 Python编程等级考试三级真题解析(判断题)2020年12月
    基于SVM+TensorFlow+Django的酒店评论打分智能推荐系统——机器学习算法应用(含python工程源码)+数据集+模型(二)
    Istio Ambient Mesh七层服务治理图文详解
  • 原文地址:https://blog.csdn.net/weixin_45985053/article/details/126022837