我正在研究Spring OAuth,遇到CORS和预检请求方面的麻烦,也许有人可以帮助我。
我以《 Cloud Native Java》一书中的示例项目为基础: https://github.com/cloud-native-java/edge
对于我的问题,两个部分相关:网关SSO服务(greetings-client)和授权服务(auth-service)。
这是SSO配置:
@Configuration
@EnableOAuth2Sso
class SsoConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests()
.antMatchers( "/", "/app.js", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().logout().logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
边缘服务具有简单的UI部分。直接被调用时,它将尝试向受保护的 / user 端点发出请求,以获取主体信息。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>Edge Service</title>
<meta name="description" content=""/>
<meta name="viewport" content="width=device-width"/>
<base href="/"/>
<script type="text/javascript"
src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript"
src="/webjars/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript"
src="/webjars/angularjs/angular.min.js"></script>
</head>
<body ng-app="app" ng-controller="home as home">
<div class="container" ng-show="!home.authenticated">
<a href="/login">Login </a>
</div>
<div class="container" ng-show="home.authenticated">
<!--1-->
Logged in as:
<b><span ng-bind="home.user"></span></b> <br/>
Token:
<b><span ng-bind="home.token"></span> </b><br/>
Greeting from Zuul Route: <b>
<span ng-bind="home.greetingFromZuulRoute"></span></b> <br/>
Greeting from Edge Service (Feign):
<b><span ng-bind="home.greetingFromEdgeService"></span></b><br/>
</div>
<!--2-->
<script type="text/javascript" src="app.js"></script>
</body>
</html>
和javascript:
var app = angular.module("app", []);
//<1>
app.factory('oauth', function () {
return {details: null, name: null, token: null};
});
app.run(['$http', '$rootScope', 'oauth', function ($http, $rootScope, oauth) {
$http.get("/user").success(function (data) {
oauth.details = data.userAuthentication.details;
oauth.name = oauth.details.name;
oauth.token = data.details.tokenValue;
// <2>
$http.defaults.headers.common['Authorization'] = 'bearer ' + oauth.token;
// <3>
$rootScope.$broadcast('auth-event', oauth.token);
});
}]);
app.controller("home", function ($http, $rootScope, oauth) {
var self = this;
self.authenticated = false;
// <4>
$rootScope.$on('auth-event', function (evt, ctx) {
self.user = oauth.details.name;
self.token = oauth.token;
self.authenticated = true;
var name = window.prompt('who would you like to greet?');
// <5>
$http.get('/greetings-service/greet/' + name)
.success(function (greetingData) {
self.greetingFromZuulRoute = greetingData.greeting;
})
.error(function (e) {
console.log('oops!' + JSON.stringify(e));
});
// <6>
$http.get('/lets/greet/' + name)
.success(function (greetingData) {
self.greetingFromEdgeService = greetingData.greeting;
})
.error(function (e) {
console.log('oops!' + JSON.stringify(e));
});
});
});
可以预期,将启动登录过程并显示登录表单。
实际结果:浏览器被重定向到授权服务器并遇到CORS错误
从原点“ http://localhost:9191/uaa/oauth/authorize?client_id=html5&redirect_uri=http://localhost:8082/login&response_type=code&state=1zegi7”访问“ http://localhost:8082/user”(重定向自“ http://localhost:8082”)处的XMLHttpRequest已被CORS策略阻止:对预检请求的响应没有t通过访问控制检查:它没有HTTP正常状态。
此处:localhost:8082-网关服务,localhost:9191-授权服务器。
在浏览器控制台中,我可以看到这是一个OPTIONS请求。
另一方面,如果我显式调用 / login 端点(由spring提供),它将按预期工作-出现登录表单,然后 凭据验证后,我被重定向回主页。
网关服务具有简单的servlet过滤器,我在其中明确设置了ACCESS_CONTROL_ALLOW_ORIGIN标头。
@Component
class CorsFilter implements Filter {
private final Log log = LogFactory.getLog(getClass());
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = HttpServletResponse.class.cast(res);
HttpServletRequest request = HttpServletRequest.class.cast(req);
log.info(request.getMethod());
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
问题:
我尝试过的事情:
将HttpSecurity.cors()方法与CorsConfigurationSource bean结合使用:
@Configuration
@EnableOAuth2Sso
class SsoConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().antMatcher("/**").authorizeRequests()
.antMatchers( "/", "/app.js", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().logout().logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedMethods(Arrays.asList("GET","POST","OPTIONS"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
更新:这是我用于SSO网关的属性:
spring.application.name=greetings-client
server.port=${PORT:8082}
security.oauth2.resource.userInfoUri=http://auth-service/uaa/user
spring.mvc.dispatch-options-request=true
zuul.routes.hi.path=/lets/**
zuul.routes.hi.serviceId=greetings-service
management.security.enabled=false
zuul.ignoredServices=*
eureka.instance.preferIpAddress=true
eureka.instance.leaseRenewalIntervalInSeconds=10
这是Auth服务的属性:
server.port=${PORT:9191}
spring.application.name=auth-service
server.context-path=/uaa
security.sessions=if_required
logging.level.org.springframework.security=DEBUG
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
eureka.instance.preferIpAddress=true
eureka.instance.leaseRenewalIntervalInSeconds=10
和授权服务器配置:
@Configuration
@EnableAuthorizationServer
class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final ClientDetailsService clientDetailsService;
@Autowired
public AuthorizationServerConfiguration(
AuthenticationManager authenticationManager,
ClientDetailsService clientDetailsService) {
this.authenticationManager = authenticationManager;
this.clientDetailsService = clientDetailsService;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// <1>
clients.withClientDetails(this.clientDetailsService);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// <2>
endpoints.authenticationManager(this.authenticationManager);
}
}