Spring OAuth安全性-隐式流程

时间:2019-03-05 08:25:00

标签: spring spring-security spring-security-oauth2

是否可以通过Spring安全性实现OAuth隐式流程?我想在同一应用程序中创建身份验证和资源服务器。我需要用于身份验证和授权的标准auth端点,以及一些用于与用户进行处理的自定义端点(创建/更新/列表...)。

要求:

  • 内隐流
  • 自定义登录页面(/ my_login_page)
  • 用于获取令牌的静默模式(/ oauth / authorize?...&prompt = none)
  • 具有OAuth(/用户)的安全自定义端点

我坚持配置。无论我做什么,上述要求永远无法协同工作。

春天WebSecurityConfig

@Configuration
@Order(-10)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private MyAuthenticationProvider authenticationProvider;
    private MyAuthenticationDetailsSource authenticationDetailsSource;

    @Autowired
    public SecurityConfig(MyAuthenticationProvider authenticationProvider, MyAuthenticationDetailsSource authenticationDetailsSource) {
        this.authenticationProvider = authenticationProvider;
        this.authenticationDetailsSource = authenticationDetailsSource;
    }

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

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .authenticationProvider(authenticationProvider);
    }

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

            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER)
            .sessionFixation().newSession()
            .and()

            .authorizeRequests()
            .antMatchers("/assets/**", "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/v2/**").permitAll()
            .anyRequest().authenticated()
            .and()

            .formLogin()
            .loginPage("/my_login_page")
            .loginProcessingUrl("/my_process_login")
            .usernameParameter("my_username")
            .passwordParameter("pmy_assword")
            .authenticationDetailsSource(authenticationDetailsSource)
            .permitAll();
    }
}

春天AuthorizationServerConfig

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private ResourceLoader resourceLoader;
    private AuthProps authProps;

    @Autowired
    public OAuth2AuthorizationServerConfig(ResourceLoader resourceLoader, AuthProps authProps) {
        this.resourceLoader = resourceLoader;
        this.authProps = authProps;
    }

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

    @Bean
    @Qualifier("jwtAccessTokenConverter")
    public JwtAccessTokenConverter accessTokenConverter() {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resourceLoader.getResource(authProps.getAuthServerPrivateCertPath()), authProps.getAuthServerPrivateCertKey().toCharArray());
        JwtAccessTokenConverter converter = new MYJwtAccessTokenConverter();   
        converter.setKeyPair(keyStoreKeyFactory
            .getKeyPair(authProps.getAuthServerPrivateCertAlias()));

        final Resource resource = resourceLoader.getResource(authProps.getAuthServerPublicCertPath());
        String publicKey;
        try {
            publicKey = IOUtils.toString(resource.getInputStream());
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        converter.setVerifierKey(publicKey);

        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
            .tokenStore(tokenStore())
            .accessTokenConverter(accessTokenConverter());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
       oauthServer
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("my-secured-client")
            .secret("foo")
            .authorizedGrantTypes("implicit")
            .scopes("read", "write")
            .resourceIds("my-resource")
            .authorities("CLIENT")
            .redirectUris(
                    "http://localhost:4200"
            )
            .accessTokenValiditySeconds(300)
            .autoApprove(true);
    }
}

春天ResourceServerConfig

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private AuthProps authProps;
    private TokenStore tokenStore;
    private DefaultTokenServices tokenServices;

    @Autowired
    public OAuth2ResourceServerConfig(AuthProps authProps, TokenStore tokenStore, DefaultTokenServices tokenServices) {
        this.authProps = authProps;
        this.tokenStore = tokenStore;
        this.tokenServices = tokenServices;
    }

    @Override
    public void configure(final ResourceServerSecurityConfigurer config) {
        config
            .resourceId("my-resource")
                .tokenStore(tokenStore)
                .tokenServices(tokenServices);
    }

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS).permitAll()
            .antMatchers("/**").authenticated()
            .and()
            .csrf().disable();
    }
}

我将WebSecurityConfig放在了ResourceServerConfig之前,否则登录页面不起作用。但是现在我无法访问用户的自定义端点(我已重定向到登录页面)。如果我将ResourceServerConfig放在WebSecurityConfig登录页面之前停止工作。提交登录页面表单时收到404找不到响应。

我在使用静音模式来获取新的访问令牌方面也遇到了问题。使用仍然有效的/oauth/authorize调用access_token时,我将重定向到登录页面。

2 个答案:

答案 0 :(得分:0)

最后我找到了解决方法:

  1. ResourceServerConfig必须先于WebSecurityConfig
  2. loginProcessingUrl应该是/oauth/authorize
  3. 默认情况下,静音刷新在会话有效之前有效(登录表单)
  4. 用于注销的自定义端点,其中使当前会话无效

答案 1 :(得分:0)

除了@ user3714967答案之外,我还添加了一些提示,也许它可以帮助某人。问题是我们要定义多个HttpSecurity(resourceServer是WebSecurityConfigurerAdapter,顺序为3)。解决方案是将HttpSecurity.requestMatchers()与特定值一起使用。

示例

头等舱:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers().antMatchers("url1", "url2", ...).and()
            .authorizeRequests()
                .antMatchers(...).and()...
    }
}

第二堂课

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
   @Override
    public void configure(HttpSecurity http) throws Exception {
         @Override
         protected void configure(HttpSecurity http) throws Exception {
             http
               .requestMatchers().antMatchers("url3", "url4", ...)
                .and()
                 .authorizeRequests()
                    .antMatchers(...).and()...
    }
   }
 }

当我们的流量超过了(对于我的情况,密码&&隐式流量)时,这将很有用。