spring boot OAuth2.0自定义授权模式

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);
}

}

上一篇:「首席工程师」首席(Principal )工程师修炼之道


下一篇:Linux基础应用运维命令实践 笔记一