Spring OAuth security - неявный поток - PullRequest
0 голосов
/ 05 марта 2019

Возможно ли реализовать неявный поток OAuth с пружинной безопасностью?Я хочу создать сервер аутентификации и ресурсов в одном приложении.Мне нужны стандартные конечные точки аутентификации для аутентификации и авторизации и некоторые пользовательские конечные точки для работы с пользователями (создание / обновление / список ...).

Требования:

  • неявный поток
  • пользовательская страница входа (/ my_login_page)
  • тихий режим для получения токена (/oauth/authorize?...&prompt=none)
  • защищенные пользовательские конечные точки с OAuth (/ users)

Я застрял с конфигурацией.Что бы я ни делал, вышеуказанные требования никогда не работают вместе.

Пружина 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 голосов
/ 14 июня 2019

В дополнение к ответу @ user3714967 добавляю несколько советов, может быть, это кому-нибудь поможет.Проблема в том, что мы определяем несколько HttpSecurity (ресурс-сервер - это 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()...
    }
   }
 }

Это будет полезно, когда у нас больше потока (пароль и& неявные потоки для моего случая).

0 голосов
/ 09 марта 2019

Наконец-то я нашел решение:

  1. ResourceServerConfig должно быть раньше WebSecurityConfig
  2. loginProcessingUrl должно быть /oauth/authorize
  3. Тихое обновлениеработает по умолчанию, пока сеанс не будет действительным (форма входа в систему)
  4. Настраиваемая конечная точка для выхода из системы, при которой недействителен текущий сеанс

РЕДАКТИРОВАНИЕ:

@Configuration
@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) {
        auth
                .authenticationProvider(authenticationProvider);
    }

    @Override
    public void configure(WebSecurity web) {
        web
                .debug(true)
                .ignoring()
                .antMatchers(HttpMethod.OPTIONS)
                .antMatchers("/my-custom-login-page", "/my-custom-logout-page")
                .antMatchers("/assets/**", "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/v2/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()

                .authorizeRequests()
                .anyRequest().authenticated()
                .and()

                .formLogin()
                .loginPage("/my-custom-login-page")
                .loginProcessingUrl("/oauth/authorize")
                .usernameParameter("myUsernameParam")
                .passwordParameter("myPasswordParam")
                .authenticationDetailsSource(authenticationDetailsSource)
                .permitAll()
                .and()

                .csrf().disable();
    }
}

@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(authProps.getAuthServerClientId())
                .secret(authProps.getAuthServerClientSecret())
                .authorizedGrantTypes("implicit")
                .scopes("read", "write")
                .resourceIds(authProps.getAuthServerResourceId())
                .authorities("CLIENT")
                .redirectUris(
                        "http://localhost:4200/#/login",
                        "http://localhost:4200/assets/silent-refresh.html",
                        "http://localhost:8080/my-api/webjars/springfox-swagger-ui/oauth2-redirect.html"
                )
                .accessTokenValiditySeconds(authProps.getAuthServerAccessTokenValiditySeconds())
                .autoApprove(true);
    }
}

@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(authProps.getAuthServerResourceId()).tokenStore(tokenStore);
        config.resourceId(authProps.getAuthServerResourceId()).tokenServices(tokenServices);
    }

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()

                .authorizeRequests()
                .anyRequest().hasRole(AppRole.ROLE_APP_USER.split("ROLE_")[1])
                .and()

                .csrf().disable();
    }
}

@Controller
public class MainController {

    @Autowired
    public MainController() {
        ...
    }

    @GetMapping("/my-custom-login-page")
    public ModelAndView loginPage(HttpServletRequest request, HttpServletResponse response) {
        ModelAndView mv = new ModelAndView("login-page");
        return mv;
    }

    @GetMapping("/my-custom-logout-page")
    public ModelAndView logoutPage(HttpServletRequest request) {
        ModelAndView mv = new ModelAndView("logout-page");

        HttpSession session = request.getSession(false);
        if (Objects.isNull(session)) {
            mv.addObject("msg", "NO SESSION");
            return mv;
        }
        session.invalidate();
        mv.addObject("msg", "SUCCEEDED");
        return mv;
    }
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...