Spring Security OAuth2 - 需要澄清并帮助配置隐式流

时间:2016-12-28 13:42:07

标签: spring spring-security spring-security-oauth2 oauth2 spring-oauth2

我正在努力配置Spring Security OAuth2来支持隐式流(我没有密码或授权代码的问题)。

这些是不同的终点:

授权服务器

http://localhost:8082/oauth/authorize
http://localhost:8082/oauth/token
...

资源服务器

http://localhost:8081/users (protected resource)

客户端

http://localhost:8080/api/users调用http://localhost:8081/users发起OAuth2舞蹈。

我看到的是

  1. http://localhost:8080/api/users会在网址中被重定向到授权服务器:http://localhost:8082/oauth/authorize?client_id=themostuntrustedclientid&response_type=token&redirect_uri=http://localhost:8080/api/accessTokenExtractor

  2. 系统会在OAuth批准屏幕上提示我,我会授予所有范围。然后,浏览器会重定向到redirect_urihttp://localhost:8080/api/accessTokenExtractor ,其中包含一个包含access_token 的片段:http://localhost:8080/api/accessTokenExtractor#access_token=3e614eca-4abe-49a3-bbba-1b8eea05c147&token_type=bearer&expires_in=55&scope=read%20write

  3. 问题:

    一个。我如何自动恢复原始请求的执行?

    规范定义了这种行为,将access_token作为URL中的一个片段:由于片段不是直接发送到服务器,我们必须使用网页脚本来提取它并将其发送给客户端(我的春天) -mvc应用程序)。这意味着设置redirect_uri指向脚本,而不是原始请求:

    http://localhost:8080/api/accessTokenExtractor#access_token=3e614eca-4abe-49a3-bbba-1b8eea05c147&token_type=bearer&expires_in=55&scope=read%20write

    accessTokenExtractor网页将令牌发送给客户端。问题是我没有原始电话(http://localhost:8080/api/users)了......

    湾您可以在下面看到客户端调用:

        restTemplate.getOAuth2ClientContext().getAccessTokenRequest()
            .setAll(['client_id': 'themostuntrustedclientid',
                     'response_type': 'token', 
                     'redirect_uri': 'http://localhost:8080/api/accessTokenExtractor'])
    
        HttpHeaders headers = new HttpHeaders()
        ResponseEntity<List<String>> response = restTemplate.exchange('http://localhost:8081/users', HttpMethod.GET, null, new ParameterizedTypeReference<List<String>>(){}, [])
        response.getBody()
    

    如果我没有手动设置授权服务器抱怨的参数client_id,response_type和redirect_uri(UserRedirectRequiredException所必需的),则需要它们。 我们是否希望手动设置?

    奇怪的是,它们在ImplicitAccessorProvider.obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)中可用:

    ImplicitResourceDetails resource = (ImplicitResourceDetails) details;
        try {
            ...
    

    resource包含所有这些内容,但不会将其复制到请求中。

    如果我们在这里与AuthorizationCodeAccessTokenProvider进行比较,私有方法getRedirectForAuthorization()会自动执行此操作... 为何与众不同?

    配置:

    授权服务器配置

    @EnableAuthorizationServer
    @SpringBootApplication
    class Oauth2AuthorizationServerApplication {
    
        static void main(String[] args) {
            SpringApplication.run Oauth2AuthorizationServerApplication, args
        }
    }
    
    @Configuration
    class OAuth2Config extends AuthorizationServerConfigurerAdapter{
    
        @Autowired
        private AuthenticationManager authenticationManager
    
        @Bean
        public UserDetailsService userDetailsService() throws Exception {
            InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager([])
            manager.createUser(new User("jose","mypassword", [new SimpleGrantedAuthority("ROLE_USER")]))
            manager.createUser(new User("themostuntrustedclientid","themostuntrustedclientsecret", [new SimpleGrantedAuthority("ROLE_USER")]))
            return manager
        }
    
        @Bean
        public TokenStore tokenStore() {
            return new InMemoryTokenStore();
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
    
            //curl trustedclient:trustedclientsecret@localhost:8082/oauth/token -d grant_type=password -d username=user -d password=cec31d99-e5ee-4f1d-b9a3-8d16d0c6eeb5 -d scope=read
            .withClient("themostuntrustedclientid")
                .secret("themostuntrustedclientsecret")
                .authorizedGrantTypes("implicit")
                .authorities("ROLE_USER")
                .scopes("read", "write")
                .accessTokenValiditySeconds(60)
    
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(this.authenticationManager);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            //security.checkTokenAccess('hasRole("ROLE_RESOURCE_PROVIDER")')
            security.checkTokenAccess('isAuthenticated()')
        }
    }
    

    资源服务器配置和受保护的端点:

    @EnableResourceServer
    @SpringBootApplication
    class Oauth2ResourceServerApplication {
    
        static void main(String[] args) {
            SpringApplication.run Oauth2ResourceServerApplication, args
        }
    }
    
    @Configuration
    class OAuth2Config extends ResourceServerConfigurerAdapter{
    
        @Value('${security.oauth2.resource.token-info-uri}')
        private String checkTokenEndpointUrl
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                // Since we want the protected resources to be accessible in the UI as well we need
                // session creation to be allowed (it's disabled by default in 2.0.6)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .and()
                .requestMatchers().antMatchers("/users/**")
            .and()
                .authorizeRequests()
                    .antMatchers(HttpMethod.GET, "/users").access("#oauth2.hasScope('read')")
                    .antMatchers(HttpMethod.PUT, "/users/**").access("#oauth2.hasScope('write')")
        }
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            RemoteTokenServices remoteTokenServices = new RemoteTokenServices()
            remoteTokenServices.setCheckTokenEndpointUrl(checkTokenEndpointUrl)
            remoteTokenServices.setClientId("usersResourceProvider")
            remoteTokenServices.setClientSecret("usersResourceProviderSecret")
            resources.tokenServices(remoteTokenServices)
        }
    }
    
    @RestController
    class UsersRestController {
    
        private Set<String> users = ["jose", "ana"]
    
        @GetMapping("/users")
        def getUser(){
            return users
        }
    
        @PutMapping("/users/{user}")
        void postUser(@PathVariable String user){
            users.add(user)
        }
    
    }
    

    这是客户端配置

    @EnableOAuth2Client
    @SpringBootApplication
    class SpringBootOauth2ClientApplication {
    
        static void main(String[] args) {
            SpringApplication.run SpringBootOauth2ClientApplication, args
        }
    }
    
    @Configuration
    class SecurityConfig extends WebSecurityConfigurerAdapter{
    
        @Autowired
        public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
            auth.eraseCredentials(false)
                .inMemoryAuthentication().withUser("jose").password("mypassword").roles('USER')
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
            .authorizeRequests()
            .anyRequest().hasRole('USER')
            .and()
            .formLogin()
        }
    
    }
    
    @Configuration
    class OAuth2Config {
    
        @Value('${oauth.resource:http://localhost:8082}')
        private String baseUrl
    
        @Value('${oauth.authorize:http://localhost:8082/oauth/authorize}')
        private String authorizeUrl
    
        @Value('${oauth.token:http://localhost:8082/oauth/token}')
        private String tokenUrl
    
        @Autowired
        private OAuth2ClientContext oauth2Context
    
        @Bean
        OAuth2ProtectedResourceDetails resource() {
            ImplicitResourceDetails resource = new ImplicitResourceDetails()
            resource.setAuthenticationScheme(AuthenticationScheme.header)
            resource.setAccessTokenUri(authorizeUrl)
            resource.setUserAuthorizationUri(authorizeUrl);
            resource.setClientId("themostuntrustedclientid")
            resource.setClientSecret("themostuntrustedclientsecret")
            resource.setScope(['read', 'write'])
            resource
        }
    
        @Bean
        OAuth2RestTemplate restTemplate() {
            OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resource(), oauth2Context)
            //restTemplate.setAuthenticator(new ApiConnectOAuth2RequestAuthenticator())
            restTemplate
        }
    }
    

    我的客户端具有以下控制器,它从资源服务器调用受保护的aouth2端点:

    @RestController
    class ClientRestController {
    
        @Autowired
        private OAuth2RestTemplate restTemplate
    
        def exceptionHandler(InsufficientScopeException ex){
            ex
        }
    
        @GetMapping("/home")
        def getHome(HttpSession session){
            session.getId()
        }
    
        @GetMapping("/users")
        def getUsers(HttpSession session){
            println 'Session id: '+ session.getId()
    
            //TODO Move to after authentication
            Authentication auth = SecurityContextHolder.getContext().getAuthentication()
            restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setAll(['client_id': 'themostuntrustedclientid', 'response_type': 'token', 'redirect_uri': 'http://localhost:8080/api/users'])
    
            HttpHeaders headers = new HttpHeaders()
            ResponseEntity<List<String>> response = restTemplate.exchange('http://localhost:8081/users', HttpMethod.GET, null, new ParameterizedTypeReference<List<String>>(){}, [])
    
    
            response.getBody()
        }
    }
    

0 个答案:

没有答案