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

Java 在Spring Boot中将Oauth2与formlogin和执行器安全性相结合,java,spring-boot,spring-security,spring-oauth2,Java,Spring Boot,Spring Security,Spring Oauth2,我使用的是Spring Boot 1.5.9,并且有一个应用程序,该应用程序的API使用OAuth2客户机凭据,在同一个Spring Boot应用程序中使用Thymeleaf的CMS的formlogin 为了实现这一点,我有以下bean来配置表单登录: @Configuration public class WebSecurityGlobalConfig extends WebSecurityConfigurerAdapter { @Autowired private User

我使用的是Spring Boot 1.5.9,并且有一个应用程序,该应用程序的API使用OAuth2客户机凭据,在同一个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
中为执行器端点设置的自定义上下文路径):

对于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);
        }
    }
}
我想要以下行为:

  • 如果用户通过使用Oauth2访问令牌拥有角色
    ADMIN
    ,则必须可以访问所有执行器端点
  • 如果用户没有此
    管理员
    角色,则只能访问
    /health
    /info
    (如果
    管理员
    /health
    应该像默认情况一样显示额外信息)
当前的行为:

每个人都可以查看信息和健康端点,但作为管理员,您不会获得额外的信息。对于其他端点,如果我尝试使用管理员用户的访问令牌,则会得到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
,则管理员用户具有访问权限,但所有非管理员用户也具有访问权限


我应该更改什么以获得想要的行为?

我在
ResourceServerConfiguration
configure
方法中成功地使用了以下功能:

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()

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

@WimDeblauwe。该任务可以通过引入两个安全链来完成。请看下面的文章
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()