代码之家  ›  专栏  ›  技术社区  ›  Jamie White

使用传统Spring MVC的Spring Security OAuth 2

  •  1
  • Jamie White  · 技术社区  · 7 年前

    能够检索访问令牌(步骤1)。

    使用“访问令牌”检索安全资源时获取状态401(请参阅步骤2)。

    Curl命令:

    1. 获取访问令牌:

      curl-X POST——用户clientapp:123456 http://localhost:8080/oauth/token -H“accept:application/json”-H“内容类型:application/x-www-form-urlencoded”-d“grant_type=password&username=adolfo&password=123&scope=read_profile”

    回应:

    {"access_token":"50c7c311-c73a-4a32-bc7e-6801bc64bbe0","token_type":"bearer","expires_in":41545,"scope":"read_profile"}
    
    1. 使用访问令牌检索安全数据

      卷曲-X获取 http://localhost:8080/api/profile

    回复:状态401

    The request has not been applied because it lacks valid authentication credentials for the target resource.
    

    //资源服务器配置

     @Configuration
        @EnableResourceServer
        public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
    
            @Override
            public void configure(HttpSecurity http) throws Exception {
                //@formatter:off
                http.authorizeRequests()
                        .anyRequest()
                        .authenticated()
                        .and()
                        .requestMatchers()
                        .antMatchers("/api/**");
                //@formatter:on
            }
    
        }
    

     @Configuration
        @EnableAuthorizationServer
        public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    
    
            @Autowired
            private AuthenticationManager authenticationManager;
    
            @Override
            public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                endpoints.authenticationManager(authenticationManager);
            }
    
    
            @Override
            public void configure(ClientDetailsServiceConfigurer clients)
                    throws Exception {
                clients.inMemory()
                        .withClient("clientapp")
                        .secret("123456")
                        //.autoApprove(true)
                        .redirectUris("http://localhost:9000/callback")
                        .authorizedGrantTypes("password")
                        .scopes("read_profile", "read_contacts");
            }
    
    
            @Override
            public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
                oauthServer.passwordEncoder(NoOpPasswordEncoder.getInstance());
            }
        }
    

    安全配置

    Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginPage("/signin")
                .loginProcessingUrl("/authenticate")
                .defaultSuccessUrl("/")
                .failureUrl("/signin-error")
                .permitAll();
    
    }
    
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance())
                    //.passwordEncoder(passwordEncoder())
                    .withUser("adolfo")
                    .password("123")
                    .roles("USER");
        }
    
    }
    

    @Controller
    public class UserController {
    
        @RequestMapping("/api/profile")
        public ResponseEntity<UserProfile> profile() {
            String username = (String) SecurityContextHolder.getContext()
                    .getAuthentication().getPrincipal();
            String email = username + "@mailinator.com";
            UserProfile profile = new UserProfile();
            profile.setName(username);
            profile.setEmail(email);
            return ResponseEntity.ok(profile);
        }
    

    初始化类

    public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
         @Override
         protected Class<?>[] getRootConfigClasses() {
         return new Class<?>[]{ SecurityConfig.class, OAuth2AuthorizationServer.class, OAuth2ResourceServer.class,};
         }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class<?>[]{DispatcherConfig.class};
        }
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    
        @Override
        protected Filter[] getServletFilters() {
            DelegatingFilterProxy delegatingFilterProxy =  new DelegatingFilterProxy("springSecurityFilterChain");
            HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
            return  new Filter[] {delegatingFilterProxy,hiddenHttpMethodFilter};
        }
    
    }
    

    //原木

    2018-08-13 15:50:29,333 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/token']
    2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/token'
    2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/token_key']
    2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/token_key'
    2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/check_token']
    2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/check_token'
    2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:72] No matches found
    2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/api/**']
    2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/api/**'
    2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:68] matched
    2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
    2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
    2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
    2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
    2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', GET]
    2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/logout'
    2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', POST]
    2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'POST /logout
    2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', PUT]
    2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'PUT /logout
    2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', DELETE]
    2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'DELETE /logout
    2018-08-13 15:50:29,337 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:72] No matches found
    2018-08-13 15:50:29,337 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
    2018-08-13 15:50:29,347 DEBUG [http-nio-8080-exec-10] o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter [OAuth2AuthenticationProcessingFilter.java:165] Authentication request failed: error="invalid_token", error_description="Invalid access token: d86d8105-9edf-44dd-94b6-36542cade80f"
    2018-08-13 15:50:29,365 DEBUG [http-nio-8080-exec-10] o.s.s.w.h.w.HstsHeaderWriter [HstsHeaderWriter.java:129] Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5337f7a
    2018-08-13 15:50:29,365 DEBUG [http-nio-8080-exec-10] o.s.s.o.p.e.DefaultOAuth2ExceptionRenderer [DefaultOAuth2ExceptionRenderer.java:101] Written [error="invalid_token", error_description="Invalid access token: d86d8105-9edf-44dd-94b6-36542cade80f"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@734d0eb7]
    2018-08-13 15:50:29,366 DEBUG [http-nio-8080-exec-10] o.s.s.w.c.SecurityContextPersistenceFilter [SecurityContextPersistenceFilter.java:119] SecurityContextHolder now cleared, as request processing completed
    

    here

    1 回复  |  直到 7 年前
        1
  •  0
  •   briarheart    7 年前

    我看不到 OAuth2AuthenticationProcessingFilter 在你的stacktrace里。我想是因为 @Order(2) SecurityConfig 上课。您的web安全配置与Spring的内部配置冲突( org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration @Order 注释来自 安全配置 上课。

    获得401响应的另一个原因是使用不同的 org.springframework.security.oauth2.provider.token.TokenStore :一个用于存储新创建的令牌,另一个用于身份验证。这可能是由于在实现 org.springframework.web.servlet.config.annotation.WebMvcConfigurer 参考文献(通过 ComponentScan

    假设您有三个配置类:

    1. ServletConfig 实现 org.springframework.web.servlet.config.annotation.webmvcconfiguer ;
    2. AuthorizationServerConfig ;
    3. ResourceServerConfig

    有两种方法可以配置servlet初始化器。第一个是回来 服务器配置 上课时间 getServletConfigClasses 方法:

    public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class<?>[] {AuthorizationServerConfig.class, AuthorizationServerConfig.class};
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class<?>[] {ServletConfig.class};
        }
    }
    

    通过选择这种方式,您应该确保没有将根配置中的类包含到servlet配置中(不要从 对根配置类所在的包初始化)。

    第二种方法是移动 服务器配置 到根配置类返回 null getServletConfigClasses类 :

    public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class<?>[] {ServletConfig.class, AuthorizationServerConfig.class, AuthorizationServerConfig.class};
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return null;
        }
    }
    

    只要不需要多个DispatcherServlet,选择第二种方法就完全可以了。

    推荐文章