玩转Spring Cloud Security OAuth2身份认证扩展——电话号码+验证码认证
在程序的认证过程中,除了常规的用户名和密码方式(可以参考深入理解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身份认证扩展——电话号码+验证码认证相关推荐
- gateway oauth2 对称加密_深入理解Spring Cloud Security OAuth2及JWT
因项目需要,需要和三方的oauth2服务器进行集成.网上关于spring cloud security oauth2的相关资料,一般都是讲如何配置,而能把这块原理讲透彻的比较少,这边自己做一下总结和整 ...
- 【java_wxid项目】【第七章】【Spring Cloud Security Oauth2集成】
主项目链接:https://gitee.com/java_wxid/java_wxid 项目架构及博文总结: 点击:[使用Spring Boot快速构建应用] 点击:[使用Spring Cloud O ...
- Spring Cloud Security OAuth2结合网关
文章目录 一.需求分析 二.服务搭建 2.1 注册中心 Eureka 搭建 2.2 网关 Zuul 搭建 2.2.1 转发明文token给微服务 2.2.2 微服务处理 token 2.2.3 Spr ...
- 【Spring Cloud Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权
一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间这里只贴出关键部分代码的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证 ...
- Spring Cloud Security:Oauth2实现单点登录
摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录功能,本文将对其单点登录用法进行详细介绍. 单点登录简介 单 ...
- 微服务集成cas_Spring Cloud(四) Spring Cloud Security集成CAS (单点登录)对微服务认证...
一.前言 由于leader要求在搭好的spring cloud 框架中加入对微服务的认证包括单点登录认证,来确保系统的安全,所以研究了Spring Cloud Security这个组件.在前面搭好的d ...
- 「springcloud 2021 系列」Spring Cloud Gateway + OAuth2 + JWT 实现统一认证与鉴权
通过认证服务进行统一认证,然后通过网关来统一校验认证和鉴权. 将采用 Nacos 作为注册中心,Gateway 作为网关,使用 nimbus-jose-jwt JWT 库操作 JWT 令牌 理论介绍 ...
- Spring Cloud Security:Oauth2结合JWT使用
摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2还可以实现更多功能,比如使用JWT令牌存储信息,刷新令牌功能,本文将对其结合 ...
- Spring Cloud Security:Oauth2使用入门
摘要 Spring Cloud Security 为构建安全的SpringBoot应用提供了一系列解决方案,结合Oauth2可以实现单点登录.令牌中继.令牌交换等功能,本文将对其结合Oauth2入门使 ...
最新文章
- 如何有效编写软件的75条建议
- Visual Paradigm 教程[UML]:如何使用刻板印象和标记值?(下)
- 合并远程仓库到本地_Git工作区、暂存区、本地仓库和远程仓库科普
- 设计模式--装饰者(Decorator)模式
- python划分数据集_Python数据集切分实例
- Spring是如何校验XML的
- 图文结合,白话 Go 的垃圾回收原理
- delphi打开word文件(刚刚学会的嘿嘿~)
- 【分享】Web前端开发第三方插件大全
- java静态成员方法_java的静态成员、静态方法的注意事项!
- python保存和加载数组
- 下载docker镜像包
- OpenCV-Python图片叠加与融合,cv2.add与cv2.addWeighted的区别
- 网页加载速度优化分析
- java常见问题incompatible types
- 数学建模python实现基础编程练习4
- 郭敬明的一个人的城市
- python中\t \r \s \n \f各种转移字符含义
- 通用 Mapper 4.0.0 版本发布
- 系统消息模块的设计原理
热门文章
- web全栈工程师简历
- 电子原理 半波整流与桥式整流Protues仿真电路
- Rain on your Parade(二分图匹配-Hopcroft-Carp算法)
- plsql如何导入csv文件,PLSQL导入csv文件到数据库
- 实验指南:BGP路由汇聚(下)
- JavaScript:实现ROT13密码算法(附完整源码)
- 什么是零代码开发平台,为什么企业IT应该重视?
- 【Android】使用后端云Bmob实现登录、注册
- 【毕业设计】基于单片机的心率检测系统 - stm32 物联网 嵌入式
- sd和sem啥区别_生物统计学-标准差(SD)和标准误(SEM)有何区别.pdf