第一部分:关于授权类型 grant_type
的解析
- 每种
grant_type
都会有一个对应的TokenGranter
实现类。 - 所有
TokenGranter
实现类都通过CompositeTokenGranter
中的tokenGranters
集合存起来。 - 然后通过判断
grantType
参数来定位具体使用那个TokenGranter
实现类来处理授权。
第二部分:关于授权登录逻辑
- 每种
授权方式
都会有一个对应的AuthenticationProvider
实现类来实现。 - 所有
AuthenticationProvider
实现类都通过ProviderManager
中的providers
集合存起来。 TokenGranter
类会 new 一个AuthenticationToken
实现类,如UsernamePasswordAuthenticationToken
传给ProviderManager
类。- 而
ProviderManager
则通过AuthenticationToken
来判断具体使用那个AuthenticationProvider
实现类来处理授权。 - 具体的登录逻辑由
AuthenticationProvider
实现类来实现,如DaoAuthenticationProvider
。
所有的授权类型都会继承
AbstractTokenGranter
验证码模式其实就是用户名密码模式,无非在校验验证码的时候先检查验证码是否正确
所有我们直接复制
ResourceOwnerPasswordTokenGranter
,在getOAuth2Authentication
中增加验证码相关逻辑
实现图片验证码模式
- 新建一个
CaptchaTokenGranter
继承AbstractTokenGranter
申明GRANT_TYPE为验证码类型*captcha*
,复制上诉密码模式的TokenGranter - 其中有两个构造函数,第一个是在配置CaptchaTokenGranter时调用,宁外一个是自己内部调用
package com.Lonni.oauth.granter;
import com.Lonni.common.constant.AuthConstant;
import com.Lonni.common.utils.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 图片验证码类型 tokenGranter
*/
public class CaptchaTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE="captcha";
private final AuthenticationManager authenticationManager;
private RedisTemplate redisTemplate;
public CaptchaTokenGranter(AuthenticationManager authenticationManager,
RedisTemplate redisTemplate,
AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory) {
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
this.redisTemplate=redisTemplate;
}
protected CaptchaTokenGranter(AuthenticationManager authenticationManager,
AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory,
String grantType) {
//调用父类 接管GRANT_TYPE类型
super(tokenServices, clientDetailsService, requestFactory, grantType);
this.authenticationManager=authenticationManager;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
String username = parameters.get("username");
String password = parameters.get("password");
String captcha=parameters.get("captcha");
if (StringUtils.isEmpty(captcha)){
throw new InvalidGrantException("验证码不能为空!");
}
//正式环境放行
// String key= AuthConstant.AUTH_CATCHA_CACHE_KEY+captcha;
// Object o = redisTemplate.opsForValue().get(key);
// if (o==null){
// throw new InvalidGrantException("验证码不存在!");
// }
// if (!o.toString().equals(captcha)){
// throw new InvalidGrantException("验证码错误!");
// }
// Protect from downstream leaks of password
parameters.remove("password");
Authentication userAuth = new UsernamePasswordAuthenticationToken(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());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}
建立TokenGranter扩展类,将自定义的TokenGranter加入到系统默认的列表中
package com.Lonni.oauth.granter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* TokenGranter 扩展 将自定义的grant_type类型添加到oauth2中
* 使用方法:
* 在configure(AuthorizationServerEndpointsConfigurer endpoints)中:
* //获取自定义tokenGranter
* TokenGranter tokenGranter = TokenGranterExt.getTokenGranter(authenticationManager, endpoints, baseRedis, userClient, socialProperties);
* endpoints.tokenGranter(tokenGranter);
*
*/
public class TokenGranterExt {
public static TokenGranter getTokenGranter(final AuthenticationManager authenticationManager,
final AuthorizationServerEndpointsConfigurer endpointsConfigurer,
RedisTemplate redisTemplate
) {
// 默认tokenGranter集合 security 自带的
List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpointsConfigurer.getTokenGranter()));
//添加验证码
granters.add(new CaptchaTokenGranter(authenticationManager, redisTemplate, endpointsConfigurer.getTokenServices(), endpointsConfigurer.getClientDetailsService(), endpointsConfigurer.getOAuth2RequestFactory()));
return new CompositeTokenGranter(granters);
}
}
在授权服务配置中加入所有的TokenGranter
在 AuthorizationServerConfigurerAdapter 实现类的
configure*(*AuthorizationServerEndpointsConfigurer endpoints*)*
方法增加
//获取grant_type类型组合
TokenGranter tokenGranter = TokenGranterExt.getTokenGranter(authenticationManager, endpoints, redisTemplate);
endpoints
//设置密码模式认证器
.authenticationManager(authenticationManager)
//设置授权码模式认证器
.authorizationCodeServices(this.authorizationCodeServices())
//.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource))
//设置令牌策略
.tokenServices(tokenServices())
//设置查询用户的userDetilService
.userDetailsService(userDetailsService)
//设置grant_type类型集合
.tokenGranter(tokenGranter)
//允许post get提交认证
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);