简体   繁体   English

Spring Security Web + OAuth2

[英]Spring Security Web + OAuth2

I'm trying to integrate both normal forms + session login and OAuth2 in the same Spring Boot MVC application. 我正在尝试将正常形式+会话登录和OAuth2集成到同一Spring Boot MVC应用程序中。 This may be my misunderstanding of how the Spring Security configuration works. 这可能是我对Spring Security配置工作方式的误解。 I've also tried to adapt the config in the Sparklr sample app but I just can't work it out. 我也尝试在Sparklr示例应用程序中修改配置,但是我无法解决。

I would like any url matching "/web/**" to be authenticated with forms login (including the redirect to the login page) except "resources/**", "/web/register" and of course "/login" I also need any url matching "/api/**" to require an OAuth2 token except "/api/register" 我希望任何与“ / web / **”匹配的URL都可以通过表单登录(包括“重定向到登录页面”)进行身份验证,除了“ resources / **”,“ / web / register”,当然还有“ / login”我还需要任何匹配“ / api / **”的网址来要求OAuth2令牌,但“ / api / register”除外

Currently it seems that only the ResourceServerConfigurerAdapter is taking effect. 当前,似乎只有ResourceServerConfigurerAdapter生效。 I have tried numerous combinations of rules but I cannot seem to get the effect that I want. 我尝试了多种规则组合,但似乎无法获得想要的效果。 When looking at the debug output I can see it attempting to match the OAuth endpoints and then my API endpoints but not any web endpoints. 查看调试输出时,我可以看到它尝试匹配OAuth端点,然后匹配我的API端点,但不匹配任何Web端点。

If I get the forms login working then the urls I need to be secured by OAuth2 allow anonymous access. 如果我的表单登录正常工作,则需要由OAuth2保护的网址允许匿名访问。 Any help in understanding this/getting it to work is appreciated. 感谢您对理解/使它起作用的任何帮助。

Here's my configuration: 这是我的配置:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/login", "/resources/**", "/web/register").permitAll()
                //.anyRequest().authenticated()
                .antMatchers("/web/**").authenticated()
                .and()
                .exceptionHandling().accessDeniedPage("/login?authorization_error=true")
                .and()
                .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/api/users/register")).disable()
                .logout().logoutUrl("/logout").logoutSuccessUrl("/login")
                .and()
                .formLogin().loginProcessingUrl("/login").failureUrl("/login?authorization_error=true").defaultSuccessUrl("/web/home").loginPage("/login");
    }

    @Autowired
    private CustomDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
                auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

@Configuration
public class OAuth2ServerConfig {

    private static final String TEST_RESOURCE_ID = "test";

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(TEST_RESOURCE_ID).stateless(false);             }

        @Override
        public void configure(HttpSecurity http) throws Exception {


            http
//                     Since we want the protected resources to be accessible in the UI as well we need
//                     session creation to be allowed (it's disabled by default in 2.0.6)
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                    .and().authorizeRequests()
                    .antMatchers("/api/users/register").permitAll()
                    .antMatchers("/api/**").access("#oauth2.isOAuth() and hasRole('ROLE_USER')")
            ;
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private TokenStore tokenStore;

        @Autowired
        private UserApprovalHandler userApprovalHandler;

        @Autowired
        @Qualifier("authenticationManagerBean")
        private AuthenticationManager authenticationManager;

        @Autowired
        private CustomUserDetailsService userDetailsService;

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory().withClient("testapp")
                    .resourceIds(TEST_RESOURCE_ID)
                    .authorizedGrantTypes("authorization_code", "refresh_token",
                            "password")
                    .authorities("USER")
                    .scopes("read", "write")
                    .secret("secret");
        }

        @Bean
        public TokenStore tokenStore() {
            return new InMemoryTokenStore();
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    .tokenStore(tokenStore)
                    .userApprovalHandler(userApprovalHandler)
                    .authenticationManager(authenticationManager)
                    .userDetailsService(userDetailsService);
        }

        @Bean
        @Primary
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setTokenStore(this.tokenStore);
            return tokenServices;
        }
        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.realm("test/client");
        }
    }

    protected static class Approvals {

        @Autowired
        private ClientDetailsService clientDetailsService;

        @Autowired
        private TokenStore tokenStore;

        @Bean
        public ApprovalStore approvalStore() throws Exception {
            TokenApprovalStore store = new TokenApprovalStore();
            store.setTokenStore(tokenStore);
            return store;
        }

        @Bean
        @Lazy
        @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
        public CustomUserApprovalHandler userApprovalHandler() throws Exception {
            CustomUserApprovalHandler handler = new CustomUserApprovalHandler();
            handler.setApprovalStore(approvalStore());
            handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
            handler.setClientDetailsService(clientDetailsService);
            handler.setUseApprovalStore(true);
            return handler;
        }
    }
}

See Multiple HttpSecurity 请参阅多个HttpSecurity

Create another instance of WebSecurityConfigurerAdapter. 创建WebSecurityConfigurerAdapter的另一个实例。 If the URL does not start with /api/ this configuration will be used. 如果网址不是以/ api /开头,则将使用此配置。 This configuration is considered after ApiWebSecurityConfigurationAdapter since it has an @Order value after 1 (no @Order defaults to last). 在ApiWebSecurityConfigurationAdapter之后考虑此配置,因为它的@Order值在1以后(没有@Order默认为last)。

@EnableWebSecurity
public class MultiHttpSecurityConfig {
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) { 
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER").and()
                .withUser("admin").password("password").roles("USER", "ADMIN");
    }

    @Configuration
    @Order(1)                                                        
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("/api/**")                               
                .authorizeRequests()
                    .anyRequest().hasRole("ADMIN")
                    .and()
                .httpBasic();
        }
    }

    @Configuration                                                   
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .formLogin();
        }
    }
}

You should specify antMatcher in your ResourceServerConfigurerAdapter http block: 您应该在ResourceServerConfigurerAdapter http块中指定antMatcher

@Override
public void configure(HttpSecurity http) throws Exception {
    http.sessionManagement().
            sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    http.antMatcher("/api/**").authorizeRequests()
            .antMatchers("/api/register").permitAll()

            ......

            .expressionHandler(webExpressionHandler());
    http.csrf().disable();
    http.exceptionHandling().authenticationEntryPoint(new OAuth2AuthenticationEntryPoint());

}

Hope it helps 希望能帮助到你

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM