博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

最近在项目中,使用check-token-access检查token有效性,配合security.oauth2.resource.user-info-uri来获取用户信息。

在使用这个配置后,resourceId校验就失效了,于是跟了源码,发现在类OAuth2AuthenticationManager的以下方法做的resouceId校验:

 

OAuth2Authentication auth = tokenServices.loadAuthentication(token);

 这行代码是获取当前用户的登录信息的,跟进后,是UserInfoTokenServices类里做了数据转换,在以下代码中,丢失了登录信息中保存的resourceId数据:

 

 

改进方案:

继承类UserInfoTokenServices,重写相关方法解决此问题:

public class CustomUserInfoTokenServices extends UserInfoTokenServices {
    private String tokenType = "Bearer";
    private final String clientId;
    private final AuthoritiesExtractor authoritiesExtractor = new FixedAuthoritiesExtractor();
    private final String userInfoEndpointUrl;
    private OAuth2RestOperations restTemplate;

    public CustomUserInfoTokenServices(String userInfoEndpointUrl, String clientId) {
        super(userInfoEndpointUrl, clientId);
        this.clientId = clientId;
        this.userInfoEndpointUrl = userInfoEndpointUrl;
    }

    @Override
    public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
        super.setTokenType(tokenType);
    }

    @Override
    public void setRestTemplate(OAuth2RestOperations restTemplate) {
        super.setRestTemplate(restTemplate);
        this.restTemplate = restTemplate;
    }

    

    @Override
    public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
        Map<String, Object> map = this.getMap(this.userInfoEndpointUrl, accessToken);
        if (map.containsKey("error")) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("userinfo returned error: " + map.get("error"));
            }

            throw new InvalidTokenException(accessToken);
        } else {
            return this.extractAuthentication(map);
        }
    }

    private OAuth2Authentication extractAuthentication(Map<String, Object> map) {
        Object principal = this.getPrincipal(map);
        List<GrantedAuthority> authorities = this.authoritiesExtractor.extractAuthorities(map);

        Map<String, Object> oauth2Request = (Map<String, Object>) map.get("oauth2Request");
        ArrayList<String> resourceIds = (ArrayList<String>) oauth2Request.get("resourceIds");
        Set<String> resourceIdsSet = new HashSet<>(resourceIds.size());
        resourceIdsSet.addAll(resourceIds);

        OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null, resourceIdsSet, null, null, null);
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
        token.setDetails(map);
        return new OAuth2Authentication(request, token);
    }

    private Map<String, Object> getMap(String path, String accessToken) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Getting user info from: " + path);
        }

        try {
            OAuth2RestOperations restTemplate = this.restTemplate;
            if (restTemplate == null) {
                BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
                resource.setClientId(this.clientId);
                restTemplate = new OAuth2RestTemplate(resource);
            }

            OAuth2AccessToken existingToken = restTemplate.getOAuth2ClientContext().getAccessToken();
            if (existingToken == null || !accessToken.equals(existingToken.getValue())) {
                DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(accessToken);
                token.setTokenType(this.tokenType);
                restTemplate.getOAuth2ClientContext().setAccessToken(token);
            }

            return (Map<String, Object>) restTemplate.getForEntity(path, Map.class, new Object[0]).getBody();
        } catch (Exception var6) {
            this.logger.warn("Could not fetch user details: " + var6.getClass() + ", " + var6.getMessage());
            return Collections.singletonMap("error", "Could not fetch user details");
        }
    }

然后在配置中,设置该实现类:

Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Resource
    private ResourceServerProperties properties;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
                .antMatchers(
                        "/v2/api-docs/**",
                        "/swagger-resources/**",
                        "/swagger-ui.html",
                        "/webjars/**",
                        ,"/favicon.ico"
                ).permitAll()
                .anyRequest().authenticated()
                /* .and()
                //统一自定义异常
                 .exceptionHandling()
                .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
                .accessDeniedHandler(new CustomAccessDeniedHandler())*/
                .and()
                .csrf().disable();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId(properties.getResourceId());
        resources.tokenServices(customUserInfoTokenServices());
    }


    @Bean
    public CustomUserInfoTokenServices customUserInfoTokenServices() {
        return new CustomUserInfoTokenServices(properties.getUserInfoUri(), properties.getClientId());
    }