OAuth2客户端spring-boot-starter-oauth2-client
·
spring-boot-starter-oauth2-client使用
spring-boot-starter-oauth2-client : 这个是 spring boot提供的 OAuth2/OIDC客户端自动配置的starter,, 不用的原因
它提供的是标准的oauth2规范,但是国内很多厂商都不是标准的参数,,比如,传递的参数也不一定规范,微信,qq获取资源除了需要携带access_token还需要带上openId,
- 某些平台获取user需要带额外参数 ,,就需要自定义
OAuth2UserService去获取用户 - 某些平台token返回的格式不标准,,这时候就需要改
OAuth2AccessTokenResponseClient - oauth2-client需要自己去适配这些端点,justAuth里面有现成的各个适配第三方的,不需要自己扩展
和spring security强绑定,很多系统只想要第三方用户信息,,但是spring security将获取到的用户信息封装成了 OAuth2AuthenticationToken,然后放入spring security的上下文中,,他不是简单的获取用户信息,,,oauth登录就必须走spring security的这套认证架构,,不能只用其中一部分
oauth2的登录其实也是一种Provider,去通过userService加载第三方的用户,他有两个拦截器,都是拦截指定的url,进行操作:
OAuth2AuthorizationRequestRedirectFilter: 拦截指定url,生成第三方授权链接OAuth2LoginAuthenticationFilter: 拦截指定url,,处理登录认证
代码:
认证信息:
public interface Authentication {
Object getPrincipal();
Object getCredentials();
boolean isAuthenticated();
void setAuthenticated(boolean authenticated);
}
@Data
public class OAuth2LoginAuthenticationToken implements Authentication{
private ClientRegistration clientRegistration;
private String code;
private OAuth2User user;
private boolean authenticated;
public OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration, String code) {
this.clientRegistration = clientRegistration;
this.code = code;
}
@Override
public Object getPrincipal() {
return user;
}
@Override
public Object getCredentials() {
return code;
}
@Override
public boolean isAuthenticated() {
return authenticated;
}
@Override
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
}
第三方官方信息:
/**
* oauth2 客户端信息
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ClientRegistration {
private String clientId;
private String clientSecret;
private String authorizationUri;
private String tokenUri;
private String userInfoUri;
}
/**
* 客户端仓库
* 用来多存多个 oauth2 client
*/
public class ClientRegistrationRepository {
private Map<String, ClientRegistration> registrations = new HashMap<>();
/**
* 添加一个 oauth2 的客户端
* @param id
* @param registration
*/
public void add(String id, ClientRegistration registration){
registrations.put(id,registration);
}
public ClientRegistration find(String id){
return registrations.get(id);
}
}
认证器:
public class AuthenticationManager {
private List<AuthenticationProvider> providers;
public AuthenticationManager(List<AuthenticationProvider> providers) {
this.providers = providers;
}
public Authentication authentication(Authentication authentication){
for (AuthenticationProvider provider : providers) {
if (provider.supports(authentication)) {
return provider.authenticate(authentication);
}
}
throw new RuntimeException("没有对应的provider");
}
}
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication);
boolean supports(Authentication authentication);
}
/**
* 用code 换 access_token
*/
public class OAuth2AuthorizationCodeAuthenticationProvider {
private OAuth2AccessTokenResponseClient tokenClient;
private OAuth2UserService userService;
public OAuth2AuthorizationCodeAuthenticationProvider(OAuth2AccessTokenResponseClient tokenClient, OAuth2UserService userService) {
this.tokenClient = tokenClient;
this.userService = userService;
}
public Authentication authenticate(OAuth2LoginAuthenticationToken token){
ClientRegistration registration = token.getClientRegistration();
String code = token.getCode();
OAuth2AccessToken accessToken = tokenClient.getToken(registration, code);
// 换用户信息
OAuth2User user = userService.loadUser(accessToken);
token.setAuthenticated(true);
token.setUser(user);
return token;
}
}
public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider{
/**
* 为什么要多拆一个 provider,,,是因为这个provider不止给login用,,,OAuth2的其他场景也需要,通过code换取token
*/
private OAuth2AuthorizationCodeAuthenticationProvider codeProvider;
public OAuth2LoginAuthenticationProvider(OAuth2AuthorizationCodeAuthenticationProvider codeProvider) {
this.codeProvider = codeProvider;
}
@Override
public Authentication authenticate(Authentication authentication) {
OAuth2LoginAuthenticationToken token = (OAuth2LoginAuthenticationToken) authentication;
return codeProvider.authenticate(token);
}
@Override
public boolean supports(Authentication authentication) {
return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication.getClass());
}
}
获取用户信息:
/**
* oauth2 用户
*/
@Getter
public class OAuth2User {
private String username;
public OAuth2User(String username) {
this.username = username;
}
}
public interface OAuth2UserService {
OAuth2User loadUser(OAuth2AccessToken accessToken);
}
public class DefaultOAuth2UserService implements OAuth2UserService{
@Override
public OAuth2User loadUser(OAuth2AccessToken accessToken) {
System.out.println("request userinfo with token"+accessToken.getTokenValue());
OAuth2User githubUser = new OAuth2User("githubUser");
return githubUser;
}
}
public interface OAuth2AccessTokenResponseClient {
/**
* 获取token
* @param registration
* @param code
* @return
*/
OAuth2AccessToken getToken(ClientRegistration registration,String code);
}
public class DefaultAuthorizationCodeTokenResponseClient implements OAuth2AccessTokenResponseClient{
@Override
public OAuth2AccessToken getToken(ClientRegistration registration, String code) {
System.out.println("code = " + code);
System.out.println("post "+registration.getTokenUri());
System.out.println("clientId="+registration.getClientId());
return new OAuth2AccessToken("mock_access_token");
}
}
过滤器:
/**
* 处理oauth的回调
* /login/oauth2/code/{}
*/
public class OAuth2LoginAuthenticationFilter {
private ClientRegistrationRepository repository;
private AuthenticationManager authenticationManager;
public OAuth2LoginAuthenticationFilter(ClientRegistrationRepository repository, AuthenticationManager authenticationManager) {
this.repository = repository;
this.authenticationManager = authenticationManager;
}
public void attemptAuthentication(String registrationId,String code){
ClientRegistration registration = repository.find(registrationId);
OAuth2LoginAuthenticationToken token = new OAuth2LoginAuthenticationToken(registration, code);
Authentication authentication = authenticationManager.authentication(token);
System.out.println("认证成功:"+((OAuth2User) authentication.getPrincipal()).getUsername());
}
}
测试:
public class Main {
public static void main(String[] args) {
ClientRegistration github = new ClientRegistration(
"github",
"client-id",
"secret",
"https://github.com/login/oauth/access_token","http://xxx/xx");
ClientRegistrationRepository repository = new ClientRegistrationRepository();
repository.add("github",github);
// 用code换token
DefaultAuthorizationCodeTokenResponseClient tokenClient = new DefaultAuthorizationCodeTokenResponseClient();
DefaultOAuth2UserService userService = new DefaultOAuth2UserService();
OAuth2AuthorizationCodeAuthenticationProvider codeProvider = new OAuth2AuthorizationCodeAuthenticationProvider(tokenClient, userService);
OAuth2LoginAuthenticationProvider provider = new OAuth2LoginAuthenticationProvider(codeProvider);
AuthenticationManager manager = new AuthenticationManager(List.of(provider));
OAuth2LoginAuthenticationFilter filter = new OAuth2LoginAuthenticationFilter(repository, manager);
filter.attemptAuthentication("github","mock_code");
}
}
公众号:代码源记
更多推荐



所有评论(0)