代码之家  ›  专栏  ›  技术社区  ›  Wim Deblauwe

在Spring Boot中将Oauth2与formlogin和执行器安全性相结合

  •  11
  • Wim Deblauwe  · 技术社区  · 8 年前

    我使用的是Spring Boot 1.5.9,并且有一个应用程序,该应用程序有一个使用OAuth2客户端凭据的API,还有一个在同一Spring Boot应用程序中使用Thymeleaf的CMS的formlogin。

    为此,我有以下bean来配置表单登录:

    @Configuration
    public class WebSecurityGlobalConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private UserDetailsService userDetailsService;
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
            auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder);
        }
    
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring()
               .antMatchers(HttpMethod.OPTIONS);
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                // api security is handled elsewhere (See OAuth2ServerConfiguration)
                .antMatchers("/api/**", "/oauth/**", "/management/**")
                .permitAll()
                // end api security
                .anyRequest().hasRole(UserRole.ADMIN.name())
                .and()
                .formLogin().loginPage("/login")
                .permitAll()
                .and()
                .logout().permitAll();
        }
    }
    

    因此,对于表单登录部分,我声明了与API、Oauth和/管理(我在中设置的自定义上下文路径)相关的所有内容 application.properties 对于致动器端点):

    management.context-path=/management
    management.security.roles=ADMIN
    

    对于Oauth2,我有以下内容:

    @Configuration
    public class OAuth2ServerConfiguration {
    
        private static final String RESOURCE_ID = "my-app-service";
    
        @Configuration
        @EnableResourceServer
        @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
        protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    
            @Override
            public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
                resources.resourceId(RESOURCE_ID);
            }
    
            @Override
            public void configure(HttpSecurity http) throws Exception {
    
                http.authorizeRequests()
                    .antMatchers(HttpMethod.OPTIONS, "/api/**")
                    .permitAll()
                    .and()
                    .antMatcher("/api/**")
                    .authorizeRequests()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .authorizeRequests()
                    .antMatchers("/management/health", "/management/info").permitAll()
                    .antMatchers("/management/**").hasRole(UserRole.ADMIN.name())
                    .anyRequest().authenticated();
            }
        }
    
        @Configuration
        @EnableAuthorizationServer
        protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    
            @Autowired
            private AuthenticationManager authenticationManager;
    
            @Autowired
            private UserDetailsService userDetailsService;
    
            @Autowired
            private PasswordEncoder passwordEncoder;
    
            @Autowired
            private TokenStore tokenStore;
    
            @Autowired
            private SecurityConfiguration securityConfiguration;
    
            // NOTE: If you set a new validity, you need to clear the 'oauth_access_token' table
            // in the database. Only new tokens get the new validity.
            @Value("${myapp.security.oauth.access-token-validity-seconds:43200}") // 12 hours by default
            private int accessTokenValiditySeconds;
    
            @Value("${myapp.security.oauth.refresh-token-validity-seconds:2592000}") // 30 days by default
            private int refreshTokenValiditySeconds;
    
            @Override
            public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
                security.passwordEncoder(passwordEncoder);
            }
    
            @Override
            public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                clients.inMemory()
                       .withClient(securityConfiguration.getMobileAppClientId())
                       .authorizedGrantTypes("password", "refresh_token")
                       .scopes("mobile_app")
                       .resourceIds(RESOURCE_ID)
                       .accessTokenValiditySeconds(accessTokenValiditySeconds)
                       .refreshTokenValiditySeconds(refreshTokenValiditySeconds)
                       .secret(passwordEncoder.encode(securityConfiguration.getMobileAppClientSecret()));
            }
    
            @Override
            public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                endpoints.tokenStore(tokenStore).
                        authenticationManager(authenticationManager)
                         .userDetailsService(userDetailsService);
            }
        }
    }
    

    我想要以下行为:

    • 如果用户具有角色 ADMIN 通过使用Oauth2访问令牌,所有执行器端点都必须可以访问
    • 如果用户没有此 管理员 角色,仅限 /health /info 应可访问(如果 管理员 , /健康状况 应该像默认情况一样显示额外信息)

    当前行为:

    每个人都可以查看信息和健康端点,但作为管理员,您不会获得额外的信息。对于其他端点,如果我尝试使用管理员用户的访问令牌,则会得到401:

    {
        "timestamp": "2018-01-30T13:45:26.625+0000",
        "status": 401,
        "error": "Unauthorized",
        "message": "Full authentication is required to access this resource.",
        "path": "/management/beans"
    } 
    

    如果我设置 management.security.enabled=false 然后,管理员用户可以访问,但所有非管理员用户也可以访问。

    我应该改变什么来获得想要的行为?

    1 回复  |  直到 8 年前
        1
  •  2
  •   Wim Deblauwe    8 年前

    我在 configure 方法 ResourceServerConfiguration :

    http
                .requestMatchers()
                    .antMatchers("/api/**")
                    .and()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.OPTIONS, "/api/**")
                    .permitAll()
                    .and()
                .requestMatchers()
                    .antMatchers("/api/**")
                    .and()
                    .authorizeRequests()
                    .and()
                .requestMatchers()
                    .antMatchers("/management/**")
                    .and()
                    .authorizeRequests()
                    .antMatchers("/management/health", "/management/info").permitAll()
                    .antMatchers("/management/**").hasRole(UserRole.ADMIN.name())
                .anyRequest()
                .authenticated()
    

    使用多个 antMatchers 直接在 http 对象不起作用,需要先使用 requestMatchers