Spring boot SpringBoot&x2B;祖尔+;Oauth2-OAuth2TokenRelayFilter重新授权

Spring boot SpringBoot&x2B;祖尔+;Oauth2-OAuth2TokenRelayFilter重新授权,spring-boot,netflix-zuul,spring-oauth2,Spring Boot,Netflix Zuul,Spring Oauth2,我正在将一组Spring Boot/OAuth2/Zuul应用程序从Spring Boot 1.3.8.RELEASE和Spring Cloud Brixton.SR6升级到Spring Boot 1.5.2.RELEASE和Spring Cloud Dalston.SR1。由于升级,我的应用程序停止工作。在进一步检查后,我发现我的SSO客户机在试图通过Zuul中继请求时都试图重新授权。深入挖掘问题的原因在于对OAuth2TokenReayFilter所做的更改。添加代码是为了在令牌无法授权时发

我正在将一组Spring Boot/OAuth2/Zuul应用程序从Spring Boot 1.3.8.RELEASE和Spring Cloud Brixton.SR6升级到Spring Boot 1.5.2.RELEASE和Spring Cloud Dalston.SR1。由于升级,我的应用程序停止工作。在进一步检查后,我发现我的SSO客户机在试图通过Zuul中继请求时都试图重新授权。深入挖掘问题的原因在于对OAuth2TokenReayFilter所做的更改。添加代码是为了在令牌无法授权时发送401错误,但是从我的观察来看,这也忽略了一个事实,即令牌可能已经被检索并且用户已经被授权

为了让我的应用程序运行,我必须通过在applications.properties中添加以下内容来禁用OAuth2TokenRelayFilter

zuul.OAuth2TokenRelayFilter.pre.disable=true
然后,我对OAuth2TokenReayFilter进行了一个小修改,并将其保存为MyAuth2TokenReayFilter。我只是在getAccessToken方法中添加了以下内容

String value = (String) ctx.get(ACCESS_TOKEN);
if ( value != null && !value.isEmpty()){
            return value ;
        }
完整代码(再次简单地从原始代码修改)

公共类MyAuth2TokenRelayFilter扩展了ZuFilter{
私有静态最终字符串访问\u令牌=“访问\u令牌”;
私有静态最终字符串TOKEN\u TYPE=“TOKEN\u TYPE”;
私有映射路由=新HashMap();
私有OAuth2RestOperations restTemplate;
公共MyAuth2TokenRelayFilter(ProxyAuthenticationProperties){
this.routes=properties.getRoutes();
}
公共void setRestTemplate(OAuth2RestOperations restTemplate){
this.restTemplate=restTemplate;
}
@凌驾
public int filterOrder(){
返回10;
}
@凌驾
公共字符串筛选器类型(){
返回“pre”;
}
@凌驾
公共布尔值shouldFilter(){
Authentication auth=SecurityContextHolder.getContext().getAuthentication();
if(OAuth2Authentication的身份验证实例){
对象详细信息=auth.getDetails();
if(详细信息OAuth2AuthenticationDetails的实例){
OAuth2AuthenticationDetails oauth=(OAuth2AuthenticationDetails)详细信息;
RequestContext ctx=RequestContext.getCurrentContext();
如果(ctx.containsKey(“代理”)){
字符串id=(字符串)ctx.get(“代理”);
if(路线、集装箱(id)){
如果(!Route.Scheme.OAUTH2.matches(routes.get(id.getScheme())){
返回false;
}
}
}
set(ACCESS_令牌,oauth.getTokenValue());
ctx.set(TOKEN_TYPE,oauth.getTokenType()==null?“Bearer”:oauth.getTokenType());
返回true;
}
}
返回false;
}
@凌驾
公共对象运行(){
RequestContext ctx=RequestContext.getCurrentContext();
ctx.addZuulRequestHeader(“授权”,ctx.get(TOKEN_TYPE)+“”+getAccessToken(ctx));
返回null;
}
私有字符串getAccessToken(RequestContext ctx){
字符串值=(字符串)ctx.get(访问令牌);
if(value!=null&&!value.isEmpty()){
返回值;
}
如果(restTemplate!=null){
//以防需要刷新
OAuth2Authentication auth=(OAuth2Authentication)SecurityContextHolder
.getContext().getAuthentication();
if(restTemplate.getResource().getClientId())
.equals(auth.getOAuth2Request().getClientId()){
试一试{
value=restTemplate.getAccessToken().getValue();
}
捕获(例外e){
//很可能是UserRedirectRequiredException,但是调用方
//可能不知道怎么处理,否则他们就不会
//使用此筛选器,因此我们将作为身份验证异常进行重试
ctx.set(“error.status_code”,HttpServletResponse.SC_UNAUTHORIZED);
抛出新的BadCredentialsException(“无法获取有效的访问令牌”);
}
}
}
返回值;
}
}
在我的应用程序中,大多数用户都是在进行zuul路由调用之前进行验证的,因此我不知道在进行路由调用并且尚未检索令牌的情况下,这是否会破坏任何东西

这是一个可行的解决方案,还是我遗漏了什么,只是掩盖了一个更大的问题

public class MyOAuth2TokenRelayFilter extends ZuulFilter {

    private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
    private static final String TOKEN_TYPE = "TOKEN_TYPE";
    private Map<String, Route> routes = new HashMap<String, Route>();

    private OAuth2RestOperations restTemplate;

    public MyOAuth2TokenRelayFilter(ProxyAuthenticationProperties properties) {
        this.routes = properties.getRoutes();
    }

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

    @Override
    public int filterOrder() {
        return 10;
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public boolean shouldFilter() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth instanceof OAuth2Authentication) {
            Object details = auth.getDetails();
            if (details instanceof OAuth2AuthenticationDetails) {
                OAuth2AuthenticationDetails oauth = (OAuth2AuthenticationDetails) details;
                RequestContext ctx = RequestContext.getCurrentContext();
                if (ctx.containsKey("proxy")) {
                    String id = (String) ctx.get("proxy");
                    if (routes.containsKey(id)) {
                        if (!Route.Scheme.OAUTH2.matches(routes.get(id).getScheme())) {
                            return false;
                        }
                    }
                }
                ctx.set(ACCESS_TOKEN, oauth.getTokenValue());
                ctx.set(TOKEN_TYPE, oauth.getTokenType()==null ? "Bearer" : oauth.getTokenType());
                return true;
            }
        }
        return false;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulRequestHeader("authorization", ctx.get(TOKEN_TYPE) + " " + getAccessToken(ctx));
        return null;
    }

    private String getAccessToken(RequestContext ctx) {
        String value = (String) ctx.get(ACCESS_TOKEN);
        if ( value != null && !value.isEmpty()){
            return value ;
        }
        if (restTemplate != null) {
            // In case it needs to be refreshed
            OAuth2Authentication auth = (OAuth2Authentication) SecurityContextHolder
                    .getContext().getAuthentication();
            if (restTemplate.getResource().getClientId()
                    .equals(auth.getOAuth2Request().getClientId())) {
                try {
                    value = restTemplate.getAccessToken().getValue();
                }
                catch (Exception e) {
                    // Quite possibly a UserRedirectRequiredException, but the caller
                    // probably doesn't know how to handle it, otherwise they wouldn't be
                    // using this filter, so we rethrow as an authentication exception
                    ctx.set("error.status_code", HttpServletResponse.SC_UNAUTHORIZED);
                    throw new BadCredentialsException("Cannot obtain valid access token");
                }
            }
        }
        return value;
    }

}