我正在将一组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中继请求时尝试重新授权。深入挖掘我的问题的原因是对OAuth2TokenRelayFilter所做的更改。如果令牌无法授权,则添加了代码以发送401错误,但是从我看来,这也忽略了令牌可能已经被检索并且用户已经被授权的事实
要让我的应用程序运行,我必须通过将以下内容添加到applications.properties来禁用OAuth2TokenRelayFilter。
zuul.OAuth2TokenRelayFilter.pre.disable=true
然后我对OAuth2TokenRelayFilter进行了一些小修改,并将其保存为MyOAuth2TokenRelayFilter。我只是将以下内容添加到getAccessToken方法中。
String value = (String) ctx.get(ACCESS_TOKEN);
if ( value != null && !value.isEmpty()){
return value ;
}
完整代码(再次简单地从原始代码修改)。
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;
}
}
在我的应用程序中,大多数用户在进行zuul路由呼叫之前都已经过验证,所以我不知道在路由调用和尚未检索到令牌的情况下这是否会中断。
这是一个可行的解决方案,还是我错过了一些东西,只是掩盖了一个更大的问题?