spring boot OAuth2.0默认自带了授权码模式,密码模式等。实际生产过程中,往往不容易满足要求,所以就需要自定义一些授权模式。
第一步:添加新的模式
如果是基于数据库保存的客户端信息,需要在要使用这种模式的客户端的authorized_grant_types字段添加一个模式类型,每种模式是按照逗号隔开。本文作为教学,添加一个my_type证码模式。
authorization_code,refresh_token,password,my_type 如此,前三种是框架自带的模式,my_type是自定义的一种模式,可以根据不同的客户端,配置相应的认证模式。例如微信就只给第三方开放了授权码模式和refresh_token模式。这些模式最终的目的都是获取access_token。
第二步:配置此模式
可以参考框架自带的密码模式来写,需要把模式换成自己定义的模式,比如my_type.
其中的UsernamePasswordAuthenticationToken这个类是spring securty自带的类,如果是自定义的模式,需要新建一个自己的token类。可以参考此文章https://blog.csdn.net/qq_40710276/article/details/112619300 自定义登录模式。本文也会详细再次介绍一遍。
示例:
public class MyTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = “my_type”;
private final AuthenticationManager authenticationManager;
public ResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, "password");
}
protected ResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
super(tokenServices, clientDetailsService, requestFactory, grantType);
this.authenticationManager = authenticationManager;
}
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
String username = (String)parameters.get("username");
String password = (String)parameters.get("password");
parameters.remove("password");
Authentication userAuth = new MyAuthenticationToken(username, password);
((AbstractAuthenticationToken)userAuth).setDetails(parameters);
Authentication userAuth;
try {
userAuth = this.authenticationManager.authenticate(userAuth);
} catch (AccountStatusException var8) {
throw new InvalidGrantException(var8.getMessage());
} catch (BadCredentialsException var9) {
throw new InvalidGrantException(var9.getMessage());
}
if (userAuth != null && userAuth.isAuthenticated()) {
OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
} else {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
}
}
第三步:新建MyAuthenticationToken
public class MyAuthenticationTokenextends AbstractAuthenticationToken {
private static final long serialVersionUID = 510L;
private final Object principal;
private Object credentials;
public MyAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public MyAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
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.credentials = null;
}
}
第四步:新建MyAuthenticationProvider
此类真正用于校验自定义的登录账号密码或是其他非账号密码。UserDetailsService是spring securty自带的类,默认可以根据用户名查询用户。也可以自定义实现。本文就不再做实现了。可以参考spring securty的JdbcDaoImpl类。
public class MyAuthenticationProvider implements AuthenticationProvider {
@Resource
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) {
MyAuthenticationToken myAuthenticationToken= (MyAuthenticationToken)authentication;
UserDetails user = userDetailsService.loadUserByMobile((String)myAuthenticationToken.getPrincipal());
if (!this.passwordEncoder.matches(authentication.getCredentials(), user .getPassword())) {
throw new BadCredentialsException("用户名或密码错误");
}
return new MyAuthenticationToken(user, user.getAuthorities());
}
}
@Override
public boolean supports(Class<?> authentication) {
return MyAuthenticationToken.class.isAssignableFrom(authentication);
}
}