Spring OAuth2 SSO预检请求处理

时间:2019-05-22 20:29:21

标签: spring spring-boot spring-security oauth-2.0 single-sign-on

我正在研究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() {
    }

}

问题:

  • 在这种情况下如何正确处理飞行前请求?
  • servlet过滤器不应该处理OPTIONS请求吗? (我没有在日志中看到它)

我尝试过的事情:

  • 使用显式servlet过滤器(如上所示)
  • 将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);
    }
}

0 个答案:

没有答案