- 定义Granter
/**
* @ClassName MobileAuthenticationProvider
* @Description TODO
* @Author CY
* @Date 2023/11/21 10:12
* @Version 1.0
*/
public class EmailPwdGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "email_password";
private final AuthenticationManager authenticationManager;
public EmailPwdGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
this.authenticationManager = authenticationManager;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
String mobile = parameters.get("email");
String password = parameters.get("password");
parameters.remove("password");
Authentication userAuth = new EmailAuthenticationToken(mobile, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
userAuth = authenticationManager.authenticate(userAuth);
} catch (AccountStatusException var8) {
throw new InvalidGrantException(var8.getMessage());
} catch (BadCredentialsException var9) {
throw new InvalidGrantException(var9.getMessage());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate mobile: " + mobile);
}
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}
cybb:Granter会将对应的Token传递给ProviderManager,进行认证
- 定义Token(基本是固定的,只需要添加一个构造方法,可以把该构造方法的参数名替换成自己的名字)
public class EmailAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
private Object credentials;
//可以添加一个构造方法,将参数名替换为相应的名字
public EmailAuthenticationToken(String email, String password) {
super(null);
this.principal = email;
this.credentials = password;
setAuthenticated(false);
}
public EmailAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
}
- 定义处理token的Provider
@Data
@Component("EmailAuthenticationProvider")
public class EmailAuthenticationProvider implements AuthenticationProvider {
@Autowired
private EmailUserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) {
EmailAuthenticationToken authenticationToken = (EmailAuthenticationToken) authentication;
String mobile = (String) authenticationToken.getPrincipal();
String password = (String) authenticationToken.getCredentials();
UserDetails user = userDetailsService.loadUserByEmail(mobile);
if (user == null) {
throw new BusinessException(Code.LOGIN_ERR,"邮箱号或密码错误");
}
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new BusinessException(Code.LOGIN_ERR,"邮箱号或密码错误");
}
EmailAuthenticationToken authenticationResult = new EmailAuthenticationToken(user, password, user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
public boolean supports(Class<?> authentication) {
return EmailAuthenticationToken.class.isAssignableFrom(authentication);
}
}
在provider处对上面定义的token进行校验(如果返回了就是验证成功了),在provider处的自由度很高,可以自定义与数据库的交互,但一般是定义一个EmailUserDetailsService来进行数据库的读取
- 将该provider添加到ProviderManager中!!! 在WebSecurityConfig中
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这一步是配置默认的UsernamePassword中的UserDetailsService
auth.userDetailsService(userDetailsService);
//添加自定义的Provider到ProviderManager的provider列表中
auth.authenticationProvider(emailAuthenticationProvider);
}
- 将Granter添加到授权模式中,在AuthorizationServer中
@Override // 令牌端点的安全约束配置
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenServices(tokenService())//令牌管理服务
.authenticationManager(authenticationManager)//认证管理器
.allowedTokenEndpointRequestMethods(HttpMethod.POST)
.accessTokenConverter(accessTokenConverter)
.tokenStore(tokenStore)
.setClientDetailsService(clientDetails());//客户端信息服务
//获取原有默认的授权者(那四种模式)
ArrayList<TokenGranter> tokenGranters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
//添加扩展的验证码模式授权者
tokenGranters.add(new CaptchaTokenGranter(endpoints.getTokenServices(),endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), authenticationManager,checkCodeClient));
//添加扩展的邮箱密码模式授权者
tokenGranters.add(new EmailPwdGranter(authenticationManager,endpoints.getTokenServices(),endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
//添加扩展的短信验证码模式授权者
tokenGranters.add(new SmsCodeTokenGranter(endpoints.getTokenServices(),endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), authenticationManager));
CompositeTokenGranter compositeTokenGranter=new CompositeTokenGranter(tokenGranters);
endpoints.tokenGranter(compositeTokenGranter); //配置授权者
}
teTokenGranter compositeTokenGranter=new CompositeTokenGranter(tokenGranters);
endpoints.tokenGranter(compositeTokenGranter); //配置授权者
}