排查spring boot security oauth2使用check-token-access做token检查后,resourceId检查失效
Posted on 2021-03-09 10:33 冲杀 阅读(794) 评论(0) 编辑 收藏 举报最近在项目中,使用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()); }