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

Wim*_*uwe 10 java spring-security spring-boot spring-oauth2

我正在使用Spring Boot 1.5.9,并且有一个应用程序具有使用OAuth2客户端凭据的API,而formlogin用于在同一个Spring Boot应用程序中使用Thymeleaf的CMS.

为此,我有以下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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

management.context-path=/management
management.security.roles=ADMIN
Run Code Online (Sandbox Code Playgroud)

对于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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我想要以下行为:

  • 如果用户ADMIN使用Oauth2访问令牌具有角色,则必须可访问所有执行器端点
  • 如果用户没有这个ADMIN角色,只/health/info应访问(如ADMIN,/health应该表现出额外的信息好像是默认设置)

目前的行为:

每个人都可以查看信息和健康端点,但作为ADMIN,您无法获得额外信息.对于其他端点,如果我尝试使用ADMIN用户的访问令牌,则会获得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"
} 
Run Code Online (Sandbox Code Playgroud)

如果我设置,management.security.enabled=false则ADMIN用户具有访问权限,但所有非ADMIN用户也具有访问权限.

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

Wim*_*uwe 2

我设法使它与以下方法一起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()
Run Code Online (Sandbox Code Playgroud)

antMatchers直接在对象上使用多个http是不行的,需要先使用requestMatchers