一、目前OAUTH2的提供了四种授权类型
Authorization Code(授权码模式):授权码模式, 通过授权码获取token进行资源访问。 Implicit(简化模式):用于移动应用程序或 Web 应用程序,这种模式比授权码模式少了code环节,回调url直接附带token。 Resource Owner Password Credentials(密码模式):资源所有者和客户端之间具有高度信任时(例如,客户端是设备的操作系统的一部分,或者是一个高度特权应用程序, 比如APP, 自研终端等),因为client可能存储用户密码。 Client Credentials(客户端模式):该模式直接根据client端的id和密钥即可获取token, 不需要用户参与, 适合内部的API应用服务使用。
但是在应用开发过程中,还存在多种登录方式,比如手机验证码、二维码扫码、微信登录等,如何在OAUTH2中集成其他多种登录方式。我们拿手机验证码登录模式举例,其他类型登录模式可以按照这种方式改造。
二、WEB端手机验证码登录
首先创建一个filter命名PhoneNumberAuthenticationFilter,该filter继承AbstractAuthenticationProcessingFilter类,oauth2自带的授权模式都是继承的该类,可以仿照password授权模式的过滤器UsernamePasswordAuthenticationFilter修改,修改请求地址为/phone/login,修改自己的参数。
public class PhoneNumberAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_PHONE_KEY = "phone";
public static final String SPRING_SECURITY_FORM_CODE_KEY = "code";
private String phoneParameter = SPRING_SECURITY_FORM_PHONE_KEY;
private String codeParameter = SPRING_SECURITY_FORM_CODE_KEY;
private boolean postOnly = true;
public PhoneNumberAuthenticationFilter() {
super(new AntPathRequestMatcher("/phone/login", "POST"));
}
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String phone = obtainPhone(request);
if (phone == null) {
phone = "";
}
phone = phone.trim();
String code = obtainCode(request);
PhoneNumAuthenticationToken authRequest = new PhoneNumAuthenticationToken(
phone, code);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainPhone(HttpServletRequest request) {
return request.getParameter(phoneParameter);
}
protected String obtainCode(HttpServletRequest request) {
return request.getParameter(codeParameter);
}
protected void setDetails(HttpServletRequest request,
PhoneNumAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}
创建自己的token类PhoneNumAuthenticationToken。
public class PhoneNumAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 420L;
private final Object phone;
private Object code;
public PhoneNumAuthenticationToken(Object phone, Object code) {
super(null);
this.phone = phone;
this.code = code;
this.setAuthenticated(false);
}
public PhoneNumAuthenticationToken(Object phone, Object code, Collection extends GrantedAuthority> authorities) {
super(authorities);
this.phone = phone;
this.code = code;
super.setAuthenticated(true);
}
public Object getCredentials() {
return this.code;
}
public Object getPrincipal() {
return this.phone;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
} else {
super.setAuthenticated(false);
}
}
public void eraseCredentials() {
super.eraseCredentials();
this.code = null;
}
}
filter中要执行
this.getAuthenticationManager().authenticate(authRequest);
所以还要新增一个provider类PhoneAuthenticationProvider。
@Component
public class PhoneAuthenticationProvider implements AuthenticationProvider {
@Autowired
private IDaaSUserDetailsManager iDaaSUserDetailsManager;
@Autowired
private ValidateCodeRepository validateCodeRepository;
/**
* 手机号、验证码的认证逻辑
* @param authentication 其实就是我们封装的 PhoneNumAuthenticationToken
* @return
* @throws AuthenticationException
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
PhoneNumAuthenticationToken token = (PhoneNumAuthenticationToken) authentication;
String phone = (String) authentication.getPrincipal();// 获取手机号
String code = (String) authentication.getCredentials(); // 获取输入的验证码
//从数据库获取验证码
ValidateCode validateCode = validateCodeRepository.queryLastCodeByPhoneType(phone,"1");
if(null == validateCode){
throw new BadCredentialsException("不存在短信发送记录,请重新发送");
}
String phoneNum = validateCode.getCode();
if (!phoneNum.equals(code)) {
throw new PhoneNumBadCredentialsException("验证码不正确");
}
if(DateUtils.differenceSecond(new Date(),validateCode.getValidateTime())>0){
throw new PhoneNumBadCredentialsException("该验证码已经失效,请重新发送,验证码有效期10分钟");
}
if(validateCode.getIfUse().equals("1")){
throw new PhoneNumBadCredentialsException("该验证码已经使用,请重新发送");
}
// 2. 根据手机号查询用户信息
UserDetails userDetails = (UserDetails) iDaaSUserDetailsManager.loadUserByUsername(phone);
if (userDetails == null) {
throw new PhoneNumBadCredentialsException("用户不存在,请联系管理员开通");
}
// 3. 把用户封装到 PhoneNumAuthenticationToken 中,
// 后面就可以使用 SecurityContextHolder.getContext().getAuthentication(); 获取当前登陆用户信息
PhoneNumAuthenticationToken authenticationResult = new PhoneNumAuthenticationToken(userDetails, code, userDetails.getAuthorities());
authenticationResult.setDetails(token.getDetails());
return authenticationResult;
}
/**
* 判断是上面 authenticate 方法的 authentication 参数,是哪种类型
* Authentication 是个接口,实现类有很多,目前我们最熟悉的就是 PhoneNumAuthenticationToken、UsernamePasswordAuthenticationToken
* 很明显,我们只支持 PhoneNumAuthenticationToken,因为它封装的是手机号、验证码
* @param authentication
* @return
*/
@Override
public boolean supports(Class> authentication) {
// 如果参数是 PhoneNumAuthenticationToken 类型,返回true
return (PhoneNumAuthenticationToken.class.isAssignableFrom(authentication));
}
}
我们只需要做一个html页面,创建一个form请求,url为:/phone/login,参数用phone和code进行请求即可返回token。
三、移动端手机验证码登录
首先在oauth_client_details表authorized_grant_types增加新的授权方式,比如sms.
然后找到源码里面的AuthorizationServerEndpointsConfigurer类,在自己工程的相同目录下面创建相同类名的AuthorizationServerEndpointsConfigurer,并修改getDefaultTokenGranters方法下面添加自己的授权模式SmsTokenGranter。
public final class AuthorizationServerEndpointsConfigurer {
private AuthorizationServerTokenServices tokenServices;
private ConsumerTokenServices consumerTokenServices;
private AuthorizationCodeServices authorizationCodeServices;
private ResourceServerTokenServices resourceTokenServices;
private TokenStore tokenStore;
private TokenEnhancer tokenEnhancer;
private AccessTokenConverter accessTokenConverter;
private ApprovalStore approvalStore;
private TokenGranter tokenGranter;
private OAuth2RequestFactory requestFactory;
private OAuth2RequestValidator requestValidator;
private UserApprovalHandler userApprovalHandler;
private AuthenticationManager authenticationManager;
private ClientDetailsService clientDetailsService;
private String prefix;
private Map patternMap = new HashMap();
private Set allowedTokenEndpointRequestMethods = new HashSet();
private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping;
private boolean approvalStoreDisabled;
private List interceptors = new ArrayList();
private DefaultTokenServices defaultTokenServices;
private UserDetailsService userDetailsService;
private boolean tokenServicesOverride = false;
private boolean userDetailsServiceOverride = false;
private boolean reuseRefreshToken = true;
private WebResponseExceptionTranslator exceptionTranslator;
public AuthorizationServerTokenServices getTokenServices() {
return ProxyCreator.getProxy(AuthorizationServerTokenServices.class,
new ObjectFactory() {
@Override
public AuthorizationServerTokenServices getObject() throws BeansException {
return tokenServices();
}
});
}
public TokenStore getTokenStore() {
return tokenStore();
}
public TokenEnhancer getTokenEnhancer() {
return tokenEnhancer;
}
public AccessTokenConverter getAccessTokenConverter() {
return accessTokenConverter();
}
public ApprovalStore getApprovalStore() {
return approvalStore;
}
public ClientDetailsService getClientDetailsService() {
return ProxyCreator.getProxy(ClientDetailsService.class, new ObjectFactory() {
@Override
public ClientDetailsService getObject() throws BeansException {
return clientDetailsService();
}
});
}
public OAuth2RequestFactory getOAuth2RequestFactory() {
return ProxyCreator.getProxy(OAuth2RequestFactory.class, new ObjectFactory() {
@Override
public OAuth2RequestFactory getObject() throws BeansException {
return requestFactory();
}
});
}
public OAuth2RequestValidator getOAuth2RequestValidator() {
return requestValidator();
}
public UserApprovalHandler getUserApprovalHandler() {
return userApprovalHandler();
}
public AuthorizationServerEndpointsConfigurer tokenStore(TokenStore tokenStore) {
this.tokenStore = tokenStore;
return this;
}
public AuthorizationServerEndpointsConfigurer tokenEnhancer(TokenEnhancer tokenEnhancer) {
this.tokenEnhancer = tokenEnhancer;
return this;
}
public AuthorizationServerEndpointsConfigurer reuseRefreshTokens(boolean reuseRefreshToken) {
this.reuseRefreshToken = reuseRefreshToken;
return this;
}
public AuthorizationServerEndpointsConfigurer accessTokenConverter(AccessTokenConverter accessTokenConverter) {
this.accessTokenConverter = accessTokenConverter;
return this;
}
public AuthorizationServerEndpointsConfigurer tokenServices(AuthorizationServerTokenServices tokenServices) {
this.tokenServices = tokenServices;
if (tokenServices != null) {
this.tokenServicesOverride = true;
}
return this;
}
public boolean isTokenServicesOverride() {
return tokenServicesOverride;
}
public boolean isUserDetailsServiceOverride() {
return userDetailsServiceOverride;
}
public AuthorizationServerEndpointsConfigurer userApprovalHandler(UserApprovalHandler approvalHandler) {
this.userApprovalHandler = approvalHandler;
return this;
}
public AuthorizationServerEndpointsConfigurer approvalStore(ApprovalStore approvalStore) {
if (approvalStoreDisabled) {
throw new IllegalStateException("ApprovalStore was disabled");
}
this.approvalStore = approvalStore;
return this;
}
/**
* Explicitly disable the approval store, even if one would normally be added automatically (usually when JWT is not
* used). Without an approval store the user can only be asked to approve or deny a grant without any more granular
* decisions.
*
* @return this for fluent builder
*/
public AuthorizationServerEndpointsConfigurer approvalStoreDisabled() {
this.approvalStoreDisabled = true;
return this;
}
public AuthorizationServerEndpointsConfigurer prefix(String prefix) {
this.prefix = prefix;
return this;
}
public AuthorizationServerEndpointsConfigurer pathMapping(String defaultPath, String customPath) {
this.patternMap.put(defaultPath, customPath);
return this;
}
public AuthorizationServerEndpointsConfigurer addInterceptor(HandlerInterceptor interceptor) {
this.interceptors.add(interceptor);
return this;
}
public AuthorizationServerEndpointsConfigurer addInterceptor(WebRequestInterceptor interceptor) {
this.interceptors.add(interceptor);
return this;
}
public AuthorizationServerEndpointsConfigurer exceptionTranslator(WebResponseExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator;
return this;
}
/**
* The AuthenticationManager for the password grant.
*
* @param authenticationManager an AuthenticationManager, fully initialized
* @return this for a fluent style
*/
public AuthorizationServerEndpointsConfigurer authenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
return this;
}
public AuthorizationServerEndpointsConfigurer tokenGranter(TokenGranter tokenGranter) {
this.tokenGranter = tokenGranter;
return this;
}
/**
* N.B. this method is not part of the public API. To set up a custom ClientDetailsService please use
* {@link AuthorizationServerConfigurerAdapter#configure(ClientDetailsServiceConfigurer)}.
*/
public void setClientDetailsService(ClientDetailsService clientDetailsService) {
this.clientDetailsService = clientDetailsService;
}
public AuthorizationServerEndpointsConfigurer requestFactory(OAuth2RequestFactory requestFactory) {
this.requestFactory = requestFactory;
return this;
}
public AuthorizationServerEndpointsConfigurer requestValidator(OAuth2RequestValidator requestValidator) {
this.requestValidator = requestValidator;
return this;
}
public AuthorizationServerEndpointsConfigurer authorizationCodeServices(
AuthorizationCodeServices authorizationCodeServices) {
this.authorizationCodeServices = authorizationCodeServices;
return this;
}
public AuthorizationServerEndpointsConfigurer allowedTokenEndpointRequestMethods(HttpMethod... requestMethods) {
Collections.addAll(allowedTokenEndpointRequestMethods, requestMethods);
return this;
}
public AuthorizationServerEndpointsConfigurer userDetailsService(UserDetailsService userDetailsService) {
if (userDetailsService != null) {
this.userDetailsService = userDetailsService;
this.userDetailsServiceOverride = true;
}
return this;
}
public ConsumerTokenServices getConsumerTokenServices() {
return consumerTokenServices();
}
public ResourceServerTokenServices getResourceServerTokenServices() {
return resourceTokenServices();
}
public AuthorizationCodeServices getAuthorizationCodeServices() {
return authorizationCodeServices();
}
public Set getAllowedTokenEndpointRequestMethods() {
return allowedTokenEndpointRequestMethods();
}
public OAuth2RequestValidator getRequestValidator() {
return requestValidator();
}
public TokenGranter getTokenGranter() {
return tokenGranter();
}
public FrameworkEndpointHandlerMapping getFrameworkEndpointHandlerMapping() {
return frameworkEndpointHandlerMapping();
}
public WebResponseExceptionTranslator getExceptionTranslator() {
return exceptionTranslator();
}
private ResourceServerTokenServices resourceTokenServices() {
if (resourceTokenServices == null) {
if (tokenServices instanceof ResourceServerTokenServices) {
return (ResourceServerTokenServices) tokenServices;
}
resourceTokenServices = createDefaultTokenServices();
}
return resourceTokenServices;
}
private Set allowedTokenEndpointRequestMethods() {
// HTTP POST should be the only allowed endpoint request method by default.
if (allowedTokenEndpointRequestMethods.isEmpty()) {
allowedTokenEndpointRequestMethods.add(HttpMethod.POST);
}
return allowedTokenEndpointRequestMethods;
}
private ConsumerTokenServices consumerTokenServices() {
if (consumerTokenServices == null) {
if (tokenServices instanceof ConsumerTokenServices) {
return (ConsumerTokenServices) tokenServices;
}
consumerTokenServices = createDefaultTokenServices();
}
return consumerTokenServices;
}
private AuthorizationServerTokenServices tokenServices() {
if (tokenServices != null) {
return tokenServices;
}
this.tokenServices = createDefaultTokenServices();
return tokenServices;
}
public AuthorizationServerTokenServices getDefaultAuthorizationServerTokenServices() {
if (defaultTokenServices != null) {
return defaultTokenServices;
}
this.defaultTokenServices = createDefaultTokenServices();
return this.defaultTokenServices;
}
private DefaultTokenServices createDefaultTokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(reuseRefreshToken);
tokenServices.setClientDetailsService(clientDetailsService());
tokenServices.setTokenEnhancer(tokenEnhancer());
addUserDetailsService(tokenServices, this.userDetailsService);
return tokenServices;
}
private TokenEnhancer tokenEnhancer() {
if (this.tokenEnhancer == null && accessTokenConverter() instanceof JwtAccessTokenConverter) {
tokenEnhancer = (TokenEnhancer) accessTokenConverter;
}
return this.tokenEnhancer;
}
private AccessTokenConverter accessTokenConverter() {
if (this.accessTokenConverter == null) {
accessTokenConverter = new DefaultAccessTokenConverter();
}
return this.accessTokenConverter;
}
private TokenStore tokenStore() {
if (tokenStore == null) {
if (accessTokenConverter() instanceof JwtAccessTokenConverter) {
this.tokenStore = new JwtTokenStore((JwtAccessTokenConverter) accessTokenConverter());
}
else {
this.tokenStore = new InMemoryTokenStore();
}
}
return this.tokenStore;
}
private ApprovalStore approvalStore() {
if (approvalStore == null && tokenStore() != null && !isApprovalStoreDisabled()) {
TokenApprovalStore tokenApprovalStore = new TokenApprovalStore();
tokenApprovalStore.setTokenStore(tokenStore());
this.approvalStore = tokenApprovalStore;
}
return this.approvalStore;
}
private boolean isApprovalStoreDisabled() {
return approvalStoreDisabled || (tokenStore() instanceof JwtTokenStore);
}
private ClientDetailsService clientDetailsService() {
if (clientDetailsService == null) {
this.clientDetailsService = new InMemoryClientDetailsService();
}
if (this.defaultTokenServices != null) {
addUserDetailsService(defaultTokenServices, userDetailsService);
}
return this.clientDetailsService;
}
private void addUserDetailsService(DefaultTokenServices tokenServices, UserDetailsService userDetailsService) {
if (userDetailsService != null) {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper(
userDetailsService));
tokenServices
.setAuthenticationManager(new ProviderManager(Arrays. asList(provider)));
}
}
private UserApprovalHandler userApprovalHandler() {
if (userApprovalHandler == null) {
if (approvalStore() != null) {
ApprovalStoreUserApprovalHandler handler = new ApprovalStoreUserApprovalHandler();
handler.setApprovalStore(approvalStore());
handler.setRequestFactory(requestFactory());
handler.setClientDetailsService(clientDetailsService);
this.userApprovalHandler = handler;
}
else if (tokenStore() != null) {
TokenStoreUserApprovalHandler userApprovalHandler = new TokenStoreUserApprovalHandler();
userApprovalHandler.setTokenStore(tokenStore());
userApprovalHandler.setClientDetailsService(clientDetailsService());
userApprovalHandler.setRequestFactory(requestFactory());
this.userApprovalHandler = userApprovalHandler;
}
else {
throw new IllegalStateException("Either a TokenStore or an ApprovalStore must be provided");
}
}
return this.userApprovalHandler;
}
private AuthorizationCodeServices authorizationCodeServices() {
if (authorizationCodeServices == null) {
authorizationCodeServices = new InMemoryAuthorizationCodeServices();
}
return authorizationCodeServices;
}
private WebResponseExceptionTranslator exceptionTranslator() {
if (exceptionTranslator != null) {
return exceptionTranslator;
}
exceptionTranslator = new DefaultWebResponseExceptionTranslator();
return exceptionTranslator;
}
private OAuth2RequestFactory requestFactory() {
if (requestFactory != null) {
return requestFactory;
}
requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService());
return requestFactory;
}
private OAuth2RequestValidator requestValidator() {
if (requestValidator != null) {
return requestValidator;
}
requestValidator = new DefaultOAuth2RequestValidator();
return requestValidator;
}
private List getDefaultTokenGranters() {
ClientDetailsService clientDetails = clientDetailsService();
AuthorizationServerTokenServices tokenServices = tokenServices();
AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
OAuth2RequestFactory requestFactory = requestFactory();
List 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 SmsTokenGranter(authenticationManager, tokenServices,
clientDetails, requestFactory));
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
clientDetails, requestFactory));
}
return tokenGranters;
}
private TokenGranter tokenGranter() {
if (tokenGranter == null) {
tokenGranter = new TokenGranter() {
private CompositeTokenGranter delegate;
@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
if (delegate == null) {
delegate = new CompositeTokenGranter(getDefaultTokenGranters());
}
return delegate.grant(grantType, tokenRequest);
}
};
}
return tokenGranter;
}
private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping() {
if (frameworkEndpointHandlerMapping == null) {
frameworkEndpointHandlerMapping = new FrameworkEndpointHandlerMapping();
frameworkEndpointHandlerMapping.setMappings(patternMap);
frameworkEndpointHandlerMapping.setPrefix(prefix);
frameworkEndpointHandlerMapping.setInterceptors(interceptors.toArray());
}
return frameworkEndpointHandlerMapping;
}
}
创建手机验证码的granter类SmsTokenGranter
public class SmsTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "sms";
private final AuthenticationManager authenticationManager;
public SmsTokenGranter(AuthenticationManager authenticationManager,
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
}
protected SmsTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
super(tokenServices, clientDetailsService, requestFactory, grantType);
this.authenticationManager = authenticationManager;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
// validateCodeApplication.checkCode(parameters);
String username = parameters.get("phone");
String password = parameters.get("code");
// Protect from downstream leaks of password
parameters.remove("password");
Authentication userAuth = new PhoneNumAuthenticationToken(username, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
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 grant
throw new InvalidGrantException(e.getMessage());
}
catch (PhoneNumBadCredentialsException e){
throw new InvalidGrantException(e.getMessage());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}
在自己工程的AuthorizationServerConfigurerAdapter的实现类里面添加该granter
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsManager)
.authorizationCodeServices(authorizationCodeServices)
.tokenStore(tokenStore)
.accessTokenConverter(jwtAccessTokenConverter)
.reuseRefreshTokens(false);
List granters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
/// 添加手机验证码的授权模式
granters.add(new SmsTokenGranter(authenticationManager,endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
/// 这是一个组装模式,实现了TokenGranter接口,循环调用List中的TokenGranter组件进行校验处理,直到返回验证成功信息或者是异常信息
CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granters);
endpoints.tokenGranter(compositeTokenGranter);
}
在调用/oauth/token接口获取token时,将grant_type改为sms,增加phone和code参数即可返回token.