SpringBoot OAuth2框架本身已实现集成授权码、客户端、密码、刷新令牌等模式、现实使用中常常不能满足自身用户体系的认证,这样我们可以通过扩展授权模式的方式来实现。
一、Spring Security认证流程图
其中涉及到核心 ProviderManager、AuthenticationProvider、UserDetailsService
扩展授权模式自定义 AbstractTokenGranter、AbstractUserDetailsAuthenticationProvider实现即可,有必要的情况还可以自定义AbstractAuthenticationToken。
二、自定义AbstractAuthenticationToken
public class SmsCodeAuthenticationToken extends UsernamePasswordAuthenticationToken {
private ClientDetails ClientDetails;
public SmsCodeAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
}
三、自定义AbstractTokenGranter
public class SmsCodeTokenGranter extends AbstractTokenGranter {
public SmsCodeTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
super(authenticationManager, tokenServices, clientDetailsService, requestFactory,"sms_code");
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
TokenRequestParameter tokenRequestParameter = new TokenRequestParameter(tokenRequest);
String mobile = tokenRequestParameter.getMobile();
String smsCode = tokenRequestParameter.getCode();
tokenRequestParameter.removeCode();
if (!StringUtils.hasText(mobile)) {
throw new InvalidGrantException("the smscode grant mobile is required");
}
Authentication userAuth = new SmsCodeAuthenticationToken(mobile, smsCode);
userAuth = super.authenticate(userAuth);
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequestParameter.getTokenRequest());
return new CustomOAuth2Authentication(storedOAuth2Request, userAuth);
}
}
四、自定义AbstractUserDetailsAuthenticationProvider
public class SmsCodeAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private UserDetailsService userDetailsService;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
}
@Override
public void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks) {
super.setPostAuthenticationChecks(postAuthenticationChecks);
}
/**
* @param username 手机号
* @param authentication
* @return
* @throws AuthenticationException
*/
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
return this.getUserDetailsService().loadUserByUsername(username);
}
@Override
public boolean supports(Class<?> authentication) {
return (SmsCodeAuthenticationToken.class
.isAssignableFrom(authentication));
}
五、配置扩展
@Configuration
@Order(2)
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public SmsCodeAuthenticationProvider smsCodeAuthenticationProvider() {
SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
smsCodeAuthenticationProvider.setUserDetailsService(userInfoService);
return smsCodeAuthenticationProvider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userInfoService).passwordEncoder(passwordEncoder());
auth.authenticationProvider(smsCodeAuthenticationProvider());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer configurer) {
AuthorizationServerTokenServices tokenService = configurer.getTokenServices();
OAuth2RequestFactory requestFactory = configurer.getOAuth2RequestFactory();
ClientDetailsService clientDetailsService = configurer.getClientDetailsService();
AuthorizationCodeServices authorizationCodeServices = configurer.getAuthorizationCodeServices();
List<TokenGranter> tokenGranters = new ArrayList<>();
tokenGranters.add(new ClientCredentialsTokenGranter(tokenService, clientDetailsService, requestFactory));
tokenGranters.add(new RefreshTokenGranter(tokenService, clientDetailsService, requestFactory));
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenService, authorizationCodeServices, clientDetailsService, requestFactory));
tokenGranters.add(new ImplicitTokenGranter(tokenService, clientDetailsService, requestFactory));
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenService, clientDetailsService, requestFactory));
//短信验证码授权
tokenGranters.add(new SmsCodeTokenGranter(authenticationManager, tokenService, clientDetailsService, requestFactory));
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenGranter(tokenGranter(endpoints)); //更多自定义配置
}
}