在程序的认证过程中,除了常规的用户名和密码方式(可以参考深入理解Spring Cloud Security OAuth2身份认证),也经常会出现电话号码+密码的方式;电话号码+验证码的方式;或者第三方软件的方式。以下,以电话号码+验证码的方式讲述OAuth2认证方式的扩展。

认证对象(PhoneAndVerificationCodeAuthenticationToken )

在OAuth2认证开始认证时,会提前Authentication认证信息,然后交由AuthenticationManager认证。定义电话号码+验证码的Authentication认证信息,源码如下:

public class PhoneAndVerificationCodeAuthenticationToken extends AbstractAuthenticationToken {/*** 手机号*/private final Object phoneNumber;/*** 验证码*/private final Object verificationCode;public PhoneAndVerificationCodeAuthenticationToken(Object phoneNumber, Object verificationCode) {super(null);this.phoneNumber = phoneNumber;this.verificationCode = verificationCode;}public PhoneAndVerificationCodeAuthenticationToken(Collection<? extends GrantedAuthority> authorities, Object phoneNumber, Object verificationCode) {super(authorities);this.phoneNumber = phoneNumber;this.verificationCode = verificationCode;// 认证已经通过setAuthenticated(true);}/*** 用户身份凭证(一般是密码或者验证码)*/@Overridepublic Object getCredentials() {return verificationCode;}/*** 身份标识(一般是姓名,手机号)*/@Overridepublic Object getPrincipal() {return phoneNumber;}
}

认证逻辑(PhoneAndVerificationCodeAuthenticationProvider )

在AuthenticationManager认证过程中,是通过AuthenticationProvider接口的扩展来实现自定义认证方式的。定义手机和验证码认证提供者PhoneAndVerificationCodeAuthenticationProvider,其源码如下:

public class PhoneAndVerificationCodeAuthenticationProvider implements AuthenticationProvider {/*** UserDetailsService*/private final MallUserDetailsService mallUserDetailsService;/*** redis服务*/public final RedisTemplate<String, Object> redisTemplate;public PhoneAndVerificationCodeAuthenticationProvider(MallUserDetailsService mallUserDetailsService, RedisTemplate<String, Object> redisTemplate) {this.mallUserDetailsService = mallUserDetailsService;this.redisTemplate = redisTemplate;}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {PhoneAndVerificationCodeAuthenticationToken phoneAndVerificationCodeAuthenticationToken = (PhoneAndVerificationCodeAuthenticationToken) authentication;Object verificationCodeObj;String verificationCode = Objects.nonNull(verificationCodeObj = phoneAndVerificationCodeAuthenticationToken.getCredentials()) ?verificationCodeObj.toString() : StringUtils.EMPTY;// TODO 验证授权码// 验证用户Object phoneNumberObj;String phoneNumber = Objects.nonNull(phoneNumberObj = phoneAndVerificationCodeAuthenticationToken.getPrincipal())? phoneNumberObj.toString() : StringUtils.EMPTY;if (StringUtils.isBlank(phoneNumber)) {throw new InternalAuthenticationServiceException("phone number is empty!");}// 根据电话号码获取用户UserDetails userDetails = mallUserDetailsService.loadUserByPhoneNumber(phoneNumber);if (Objects.isNull(userDetails)) {throw new InternalAuthenticationServiceException("MallUserDetailsService returned null, which is an interface contract violation");}// 封装需要认证的PhoneAndVerificationCodeAuthenticationToken对象return new PhoneAndVerificationCodeAuthenticationToken(userDetails.getAuthorities(), phoneNumber, verificationCode);}@Overridepublic boolean supports(Class<?> authentication) {return PhoneAndVerificationCodeAuthenticationToken.class.isAssignableFrom(authentication);}
}

认证TokenGranter(PhoneAndVerificationCodeTokenGranter )

spring security OAuth2中,默认的只有授权码类型,简化类型,密码类型,客户端类型。这里需要新增一种电话号码+验证码的认证和生成访问授权码的TokenGranter。接口TokenGranter定义了token获取方法,源码如下:

public interface TokenGranter {OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest);}

spring security使用了组合模式定义了上述4中TokenGranter,如下:

  • ImplicitTokenGranter:简化类型
  • ResourceOwnerPasswordTokenGranter:密码类型
  • AuthorizationCodeTokenGranter:授权码类型
  • ClientCredentialsTokenGranter:客户端类型
  • RefreshTokenGranter:刷新token
  • CompositeTokenGranter:组合类型

CompositeTokenGranter,组合了所有的TokenGranter实现类,其源码如下:

public class CompositeTokenGranter implements TokenGranter {// TokenGranter 列表private final List<TokenGranter> tokenGranters;public CompositeTokenGranter(List<TokenGranter> tokenGranters) {this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters);}public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {for (TokenGranter granter : tokenGranters) {OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);if (grant!=null) {return grant;}}return null;}public void addTokenGranter(TokenGranter tokenGranter) {if (tokenGranter == null) {throw new IllegalArgumentException("Token granter is null");}tokenGranters.add(tokenGranter);}}

新增电话验证码类型,PhoneAndVerificationCodeTokenGranter,参考密码类型ResourceOwnerPasswordTokenGranter的认证流程,首先进行电话号码与验证码的认证,然后生成访问授权码,其源码如下:

public class PhoneAndVerificationCodeTokenGranter extends AbstractTokenGranter {private static final String GRANT_TYPE = "cms_code";private final AuthenticationManager authenticationManager;public PhoneAndVerificationCodeTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);}@Autowiredprotected PhoneAndVerificationCodeTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {super(tokenServices, clientDetailsService, requestFactory, grantType);this.authenticationManager = authenticationManager;}@Overrideprotected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());// 电话号码与验证码String phoneNumber = parameters.get("phone_number");String verificationCode = parameters.get("verification_code");Authentication userAuth = new PhoneAndVerificationCodeAuthenticationToken(phoneNumber, verificationCode);((AbstractAuthenticationToken) userAuth).setDetails(parameters);try {// authenticationManager进行验证userAuth = authenticationManager.authenticate(userAuth);} catch (AccountStatusException ase) {//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)throw new InvalidGrantException(ase.getMessage());} catch (BadCredentialsException e) {// If the username/password are wrong the spec says we should send 400/invalid grantthrow new InvalidGrantException(e.getMessage());}if (userAuth == null || !userAuth.isAuthenticated()) {throw new InvalidGrantException("Could not authenticate phone number: " + phoneNumber);}OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);return new OAuth2Authentication(storedOAuth2Request, userAuth);}
}

自定义认证配置

在资源服务中的配置,主要说下在配置tokenGranter,需要把默认端的全部一起设置,不然就是新增的才有效。tokenGranter配置的的源码如下:

    /*** 授权服务端点配置*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {// 初始化所有的TokenGranter,并且类型为CompositeTokenGranterList<TokenGranter> tokenGranters = getDefaultTokenGranters(endpoints);tokenGranters.add(new PhoneAndVerificationCodeTokenGranter(authenticationManager, endpoints.getTokenServices(),endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters))// 配置tokenStore,使用redis 存储token.tokenStore(new RedisTokenStore(redisConnectionFactory)).authenticationManager(authenticationManager)// 用户管理服务.userDetailsService(userDetailsService)// 配置令牌转化器.accessTokenConverter(jwtAccessTokenConverter)// 允许 GET、POST 请求获取 token,即访问端点:oauth/token.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST).reuseRefreshTokens(true);}/*** 初始化所有的TokenGranter*/private List<TokenGranter> getDefaultTokenGranters(AuthorizationServerEndpointsConfigurer endpoints) {ClientDetailsService clientDetails = endpoints.getClientDetailsService();AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices();AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory();List<TokenGranter> tokenGranters = new ArrayList<>();tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,requestFactory));tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);tokenGranters.add(implicit);tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));if (authenticationManager != null) {tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,clientDetails, requestFactory));}return tokenGranters;}

手机号码和验证码认证提供者的配置,源码如下:

    /*** 配置HTTP安全*/@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {// 配置电话号码和验证码认证httpSecurity.authenticationProvider(new PhoneAndVerificationCodeAuthenticationProvider(userDetailsService, redisTemplate)).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/oauth/**").permitAll().anyRequest().authenticated();}

认证测试

在测试时,在db中,配置授权类型为自定义的cms_code,client_id为meituan,client_secret为123456的客户端;配置phone_number为15198273234用户信息,模拟手机验证1234。执行postman测试成功,结果如下:

玩转Spring Cloud Security OAuth2身份认证扩展——电话号码+验证码认证相关推荐

  1. gateway oauth2 对称加密_深入理解Spring Cloud Security OAuth2及JWT

    因项目需要,需要和三方的oauth2服务器进行集成.网上关于spring cloud security oauth2的相关资料,一般都是讲如何配置,而能把这块原理讲透彻的比较少,这边自己做一下总结和整 ...

  2. 【java_wxid项目】【第七章】【Spring Cloud Security Oauth2集成】

    主项目链接:https://gitee.com/java_wxid/java_wxid 项目架构及博文总结: 点击:[使用Spring Boot快速构建应用] 点击:[使用Spring Cloud O ...

  3. Spring Cloud Security OAuth2结合网关

    文章目录 一.需求分析 二.服务搭建 2.1 注册中心 Eureka 搭建 2.2 网关 Zuul 搭建 2.2.1 转发明文token给微服务 2.2.2 微服务处理 token 2.2.3 Spr ...

  4. 【Spring Cloud Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间这里只贴出关键部分代码的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证 ...

  5. Spring Cloud Security:Oauth2实现单点登录

    摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录功能,本文将对其单点登录用法进行详细介绍. 单点登录简介 单 ...

  6. 微服务集成cas_Spring Cloud(四) Spring Cloud Security集成CAS (单点登录)对微服务认证...

    一.前言 由于leader要求在搭好的spring cloud 框架中加入对微服务的认证包括单点登录认证,来确保系统的安全,所以研究了Spring Cloud Security这个组件.在前面搭好的d ...

  7. 「springcloud 2021 系列」Spring Cloud Gateway + OAuth2 + JWT 实现统一认证与鉴权

    通过认证服务进行统一认证,然后通过网关来统一校验认证和鉴权. 将采用 Nacos 作为注册中心,Gateway 作为网关,使用 nimbus-jose-jwt JWT 库操作 JWT 令牌 理论介绍 ...

  8. Spring Cloud Security:Oauth2结合JWT使用

    摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2还可以实现更多功能,比如使用JWT令牌存储信息,刷新令牌功能,本文将对其结合 ...

  9. Spring Cloud Security:Oauth2使用入门

    摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录.令牌中继.令牌交换等功能,本文将对其结合Oauth2入门使 ...

最新文章

  1. 如何有效编写软件的75条建议
  2. Visual Paradigm 教程[UML]:如何使用刻板印象和标记值?(下)
  3. 合并远程仓库到本地_Git工作区、暂存区、本地仓库和远程仓库科普
  4. 设计模式--装饰者(Decorator)模式
  5. python划分数据集_Python数据集切分实例
  6. Spring是如何校验XML的
  7. 图文结合,白话 Go 的垃圾回收原理
  8. delphi打开word文件(刚刚学会的嘿嘿~)
  9. 【分享】Web前端开发第三方插件大全
  10. java静态成员方法_java的静态成员、静态方法的注意事项!
  11. python保存和加载数组
  12. 下载docker镜像包
  13. OpenCV-Python图片叠加与融合,cv2.add与cv2.addWeighted的区别
  14. 网页加载速度优化分析
  15. java常见问题incompatible types
  16. 数学建模python实现基础编程练习4
  17. 郭敬明的一个人的城市
  18. python中\t \r \s \n \f各种转移字符含义
  19. 通用 Mapper 4.0.0 版本发布
  20. 系统消息模块的设计原理

热门文章

  1. web全栈工程师简历
  2. 电子原理 半波整流与桥式整流Protues仿真电路
  3. Rain on your Parade(二分图匹配-Hopcroft-Carp算法)
  4. plsql如何导入csv文件,PLSQL导入csv文件到数据库
  5. 实验指南:BGP路由汇聚(下)
  6. JavaScript:实现ROT13密码算法(附完整源码)
  7. 什么是零代码开发平台,为什么企业IT应该重视?
  8. 【Android】使用后端云Bmob实现登录、注册
  9. 【毕业设计】基于单片机的心率检测系统 - stm32 物联网 嵌入式
  10. sd和sem啥区别_生物统计学-标准差(SD)和标准误(SEM)有何区别.pdf