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

时间:2018-01-30 13:50:05

标签: java spring-boot spring-security spring-oauth2

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

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

@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和/ management相关的所有内容(我在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);
        }
    }
}

我想要以下行为:

  • 如果用户使用Oauth2访问令牌拥有角色ADMIN,则必须可以访问所有执行器端点
  • 如果用户没有此ADMIN个角色,则只能访问/health/info(如果ADMIN,则/health应显示额外信息,例如{ "timestamp": "2018-01-30T13:45:26.625+0000", "status": 401, "error": "Unauthorized", "message": "Full authentication is required to access this resource.", "path": "/management/beans" } 默认情况下)

目前的行为:

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

management.security.enabled=false

如果我设置{{1}},则ADMIN用户具有访问权限,但所有非ADMIN用户也有权访问。

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

1 个答案:

答案 0 :(得分:2)

我设法在configure的{​​{1}}方法中使用以下内容:

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