自定义ResourceServerTokenServices仿照RemoteTokenServices替换RestTemplate使用dubbo实现远程鉴权回馈

自定义ResourceServerTokenServices仿照RemoteTokenServices替换RestTemplate使用dubbo实现远程鉴权回馈,第1张

自定义ResourceServerTokenServices仿照RemoteTokenServices替换RestTemplate使用dubbo实现远程鉴权回馈 1.RemoteTokenServices和RemoteTokenServices
public interface ResourceServerTokenServices {
    OAuth2Authentication loadAuthentication(String var1) throws AuthenticationException, InvalidTokenException;

    OAuth2AccessToken readAccessToken(String var1);
}

RemoteTokenServices接口定义了加载用户权限的接口而它的实现类RemoteTokenServices实现了该接口,他的实现方式是通过restTemplate构造http请求授权服务器的checkToken接口获取返回的用户权限值,其中的具体流程可以自行打断点查看源码也可以看一下这篇文章: Springsecurity-oauth2之RemoteTokenServices
因为在自己的项目中使用了微服务架构nacos+cloud alibaba+dubbo+gateway等技术 再用写死的ip地址做http请求就显得很鸡肋了,所以我打算重新构造远程鉴权部分的代码,使用dubbo替代http请求服务名和订阅机制替代ip地址
RemoteTokenServices中

 public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
        MultiValueMap formData = new linkedMultiValueMap();
        formData.add(this.tokenName, accessToken);
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", this.getAuthorizationHeader(this.clientId, this.clientSecret));
        Map map = this.postForMap(this.checkTokenEndpointUrl, formData, headers);
        if (map.containsKey("error")) {
            this.logger.debug("check_token returned error: " + map.get("error"));
            throw new InvalidTokenException(accessToken);
        } else if (!Boolean.TRUE.equals(map.get("active"))) {
            this.logger.debug("check_token returned active attribute: " + map.get("active"));
            throw new InvalidTokenException(accessToken);
        } else {
            return this.tokenConverter.extractAuthentication(map);
        }
    }
 private Map postForMap(String path, MultiValueMap formData, HttpHeaders headers) {
        if (headers.getContentType() == null) {
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        }
        Map map = (Map)this.restTemplate.exchange(path, HttpMethod.POST, new HttpEntity(formData, headers), Map.class, new Object[0]).getBody();
        return map;
    }

2.实现思路

1.在抽离的公共模块中添加dubbo接口checkToken

public interface IAouthService {
    
    void userLogoutByToken(String token);
    
    SecurityUser loadUserByUsername(String username);
    
    Map checkToken(String token);
}

2.在自己的权限服务模块中创建自定义CustomCheckTokenEndpoint类直接复制CheckTokenEndpoint代码,因为CheckTokenEndpoint原本是个controller控制器需要移除部分代码(原本的 oauthServer.allowFormAuthenticationForClients().checkTokenAccess(“permitAll()”);验证配置会无效如需添加请自行增减方法参数,在方法内自行判断,当然permitAll配置是直接放行的)
主要方法

    public Map checkToken(String value) {
        OAuth2AccessToken token = this.resourceServerTokenServices.readAccessToken(value);
        if (token == null) {
            throw new InvalidTokenException("Token was not recognised");
        } else if (token.isExpired()) {
            throw new InvalidTokenException("Token has expired");
        } else {
            OAuth2Authentication authentication = this.resourceServerTokenServices.loadAuthentication(token.getValue());
            Map response = (Map) this.accessTokenConverter.convertAccessToken(token, authentication);
            response.put("active", true);
            return response;
        }
    }

将CustomCheckTokenEndpoint注入到实现类中

import org.apache.dubbo.config.annotation.Service;
@Service
@Slf4j
public class AouthServiceImpl implements IAouthService {
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private CustomCheckTokenEndpoint checkTokenEndpoint;

    @Override
    public void userLogoutByToken(String accessToken) {
。。。。。。。。
    }

    @Override
    public SecurityUser loadUserByUsername(String username) {
。。。。。。。。。。。。。
    }

    @Override
    public Map checkToken(String token) {
        return checkTokenEndpoint.checkToken(token);
    }

}

3.子模块服务订阅授权服务(因为子模块需要调用授权模块的实现方法)

# Dubbo
dubbo:
  # 提供方应用信息,用于计算依赖关系
  application:
    name: provider-service
    # 禁用QOS同一台机器可能会有端口冲突现象
    qos-enable: false
    qos-accept-foreign-ip: false

  #订阅服务,服务提供者不写会一直警告,aouth-service提供aouth接口
  cloud:
    subscribed-services: aouth-service

4.创建CustomRemoteTokenServices类代码复制RemoteTokenServices即可,移除多余代码,添加IAouthService 接口

public class CustomRemoteTokenServices implements ResourceServerTokenServices {
    protected final Log logger = LogFactory.getLog(this.getClass());


    private String clientId;
    private String clientSecret;
    private String tokenName = "token";
    private AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
    private IAouthService aouthService;

    public CustomRemoteTokenServices() {
    }

    public void setAouthService(IAouthService aouthService) {
        this.aouthService = aouthService;
    }


    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }

    public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) {
        this.tokenConverter = accessTokenConverter;
    }

    public void setTokenName(String tokenName) {
        this.tokenName = tokenName;
    }

    public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
        MultiValueMap formData = new linkedMultiValueMap();
        formData.add(this.tokenName, accessToken);
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", this.getAuthorizationHeader(this.clientId, this.clientSecret));
        //替换restTemplate
        Map map = (Map) aouthService.checkToken(accessToken);
        if (map.containsKey("error")) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("check_token returned error: " + map.get("error"));
            }

            throw new InvalidTokenException(accessToken);
        } else if (!Boolean.TRUE.equals(map.get("active"))) {
            this.logger.debug("check_token returned active attribute: " + map.get("active"));
            throw new InvalidTokenException(accessToken);
        } else {
            return this.tokenConverter.extractAuthentication(map);
        }
    }

    public OAuth2AccessToken readAccessToken(String accessToken) {
        throw new UnsupportedOperationException("Not supported: read access token");
    }

    private String getAuthorizationHeader(String clientId, String clientSecret) {
        if (clientId == null || clientSecret == null) {
            this.logger.warn("Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error.");
        }

        String creds = String.format("%s:%s", clientId, clientSecret);

        try {
            return "Basic " + new String(base64.encode(creds.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException var5) {
            throw new IllegalStateException("Could not convert String");
        }
    }

}

5.在资源服务器中配置使用

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private static final String RESOURCE_IDS = "order";
    @Value("${security.oauth2.client.client-id}")
    private String clientId;

    @Value("${security.oauth2.client.client-secret}")
    private String secret;

    @Value("${security.oauth2.authorization.check-token-access}")
    private String checkTokenEndpointUrl;
    @Autowired
    private CustomAuthExceptionHandler customAuthExceptionHandler;
    @Autowired
    UserLogoutSuccessHandler userLogoutSuccessHandler;
    @Autowired
    CustomUserAuthenticationConverter userAuthenticationConverter;
    @Reference
    private IAouthService aouthService;

   。。。。。。。。。。。。。。。。

    @Bean
    public CustomRemoteTokenServices tokenService() {
        CustomRemoteTokenServices tokenService = new CustomRemoteTokenServices();
        tokenService.setClientId(clientId);
        tokenService.setClientSecret(secret);
        tokenService.setAouthService(aouthService);
        DefaultAccessTokenConverter defaultAccessTokenConverter = new DefaultAccessTokenConverter();
        //自定义获取用户权限对象,而不是username(没有请自行省略部分代码)
        defaultAccessTokenConverter.setUserTokenConverter(userAuthenticationConverter);
        tokenService.setAccessTokenConverter(defaultAccessTokenConverter);
        return tokenService;
    }

}

以上我使用dubbo远程调用替换了http请求,并完成了测试。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5716853.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存