OAuth2.0 - 自定义模式授权 - 短信验证码登录
Posted 小毕超
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OAuth2.0 - 自定义模式授权 - 短信验证码登录相关的知识,希望对你有一定的参考价值。
一、OAuth2.0 - 自定义模式授权
上篇文章我们分析了目前的情况,演示了微服务的大环境下在保证安全的情况下通过SpringGateWay
实现统一的鉴权处理,但是前面的演示中,我们都是基于用户名密码的方式,但是现在已经普及短信验证码登录、微信登录、QQ登录等这些第三方的登录方式,这些方式显然不在Oauth2.0提供的四种授权模式下,因此我们如果要实现第三方的登录需要自定义一个授权模式,下面我们就以短信验证码登录为例进行实现。
下面是上篇文章的地址:
二、短信验证码登录
SpringOauth2.0
虽说只有四种授权模式,但是他提供了非常高的扩展性,我们可以很方便的进行授权模式的扩展,而无需大量的改动,在SpringOauth2.0
中扩展授权模式,只需继承AbstractTokenGranter
类,并在构造方法中super
父类时,指定自己的授权标识,当用户认证传入grant_type
为该标识,则会触发该生成器的getOAuth2Authentication
方法,我们可以在这里获取客户端传入的参数,比如手机号和验证码,然后我们再和预先的验证码对比,如果OK,可以根据手机号获取用户的信息组成UsernamePasswordAuthenticationToken
对象再交给Oauth2
即可:
下面编写一个SmsCodeTokenGranter
这里只是演示功能,直接将用户和验证码写死了:
public class SmsCodeTokenGranter extends AbstractTokenGranter
private static final String SMS_GRANT_TYPE = "sms_code";
private UserService userService;
public SmsCodeTokenGranter(AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory,
UserService userService)
super(tokenServices, clientDetailsService, requestFactory, SMS_GRANT_TYPE);
this.userService = userService;
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client,
TokenRequest tokenRequest)
Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
System.out.println(parameters.toString());
// 客户端提交的手机号码
String phoneNumber = parameters.get("phone");
if (StringUtils.isBlank(phoneNumber))
throw new AccessDeniedException("get phone is null !");
// 客户端提交的验证码
String smsCode = parameters.get("code");
if (!smsCode.equals("9876"))
throw new AccessDeniedException("code err!");
UserDetails user = userService.loadUserByUsername("admin");
AbstractAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
userAuth.setDetails(parameters);
OAuth2Request oAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(oAuth2Request, userAuth);
下面还需修改AuthorizationServer
将上面的生成器配制进AuthorizationServerEndpointsConfigurer
中:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter
@Autowired
private TokenStore tokenStore;
@Autowired
@Qualifier("jdbcClientDetailsService")
private ClientDetailsService clientDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Autowired
UserService userService;
@Autowired
PasswordEncoder passwordEncoder;
@Bean("jdbcClientDetailsService")
public ClientDetailsService clientDetailsService(DataSource dataSource)
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
clientDetailsService.setPasswordEncoder(passwordEncoder);
return clientDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
clients.withClientDetails(clientDetailsService);
@Bean
public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource)
return new JdbcAuthorizationCodeServices(dataSource);//设置授权码模式的授权码如何存取
@Bean
public AuthorizationServerTokenServices tokenService()
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);
service.setTokenStore(tokenStore);
//令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
//内容增强
tokenEnhancers.add(tokenEnhancer());
tokenEnhancers.add(accessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
service.setTokenEnhancer(tokenEnhancerChain);
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
return service;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
endpoints
.authenticationManager(authenticationManager)//认证管理器
.authorizationCodeServices(authorizationCodeServices)//授权码服务
.tokenGranter(tokenGranter(endpoints))
.tokenServices(tokenService())//令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception
security
.tokenKeyAccess("permitAll()") //oauth/token_key是公开
.checkTokenAccess("permitAll()") //oauth/check_token公开
.allowFormAuthenticationForClients(); //表单认证(申请令牌)
/**
* JWT内容增强
*/
@Bean
public TokenEnhancer tokenEnhancer()
return (accessToken, authentication) ->
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("userid", -1);
Authentication auth = authentication.getUserAuthentication();
if (auth != null)
UserEntity user = (UserEntity) auth.getPrincipal();
additionalInfo.put("userid", user.getId());
else
String clientId = authentication.getOAuth2Request().getClientId();
String grantType = authentication.getOAuth2Request().getRequestParameters().get("grant_type");
if (Objects.equals(clientId, "c1") && Objects.equals(grantType, "client_credentials"))
additionalInfo.put("userid", "root");
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
;
/**
* 添加自定义授权类型
*
* @return List<TokenGranter>
*/
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints)
// endpoints.getTokenGranter() 获取SpringSecurity OAuth2.0 现有的授权类型
List<TokenGranter> granters = new ArrayList<TokenGranter>(Collections.singletonList(endpoints.getTokenGranter()));
// 构建短信验证授权类型
SmsCodeTokenGranter smsCodeTokenGranter = new SmsCodeTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory(),userService);
// 向集合中添加短信授权类型
granters.add(smsCodeTokenGranter);
// 返回所有类型
return new CompositeTokenGranter(granters);
三、测试
使用PostMan
发送POST
请求:
http://localhost:8020/oauth/token?client_id=c1&client_secret=secret&grant_type=sms_code&phone=110&code=9562
注意:grant_type
要传sms_code
可以看到返回的是Unauthorized grant type: sms_code
未授权的类型,这个是因为c1这个客户端没有sms_code
认证类型,给c1添加上sms_code
类型:
下面重新测试:
已经有我我们写的返回了,下面输入正确的验证码:
http://localhost:8020/oauth/token?client_id=c1&client_secret=secret&grant_type=sms_code&phone=110&code=9876
已经成功获取到令牌。
喜欢的小伙伴可以关注我的个人微信公众号,获取更多学习资料!
以上是关于OAuth2.0 - 自定义模式授权 - 短信验证码登录的主要内容,如果未能解决你的问题,请参考以下文章
OAuth2.0学习(4-12)spring-oauth-server分析 - 授权码模式验证过程
使用 Keycloak 构建 Java OAuth2.0 授权服务器