当前位置: 首页 > 知识库问答 >
问题:

带安全OAuth2的Spring Boot-如何使用web登录表单的资源服务器?

龙嘉玉
2023-03-14

我有一个Spring Boot(1.2.1.release)应用程序,它在一个应用程序实例中为OAuth2(2.0.6.release)授权和资源服务器提供服务。它使用自定义UserDetailsService实现,该实现利用MongoTemplate在MongoDB中搜索用户。在/oauth/token上使用grant_type=password进行身份验证,以及在调用特定资源时使用authorization:Bearer{token}头进行身份验证。

现在我想在服务器上添加简单的OAuth确认对话框,这样我就可以在API文档中对受保护资源进行身份验证和授权,例如Swagger UI调用。以下是我到目前为止所做的:

@Configuration
@SessionAttributes("authorizationRequest")
class OAuth2ServerConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/oauth/confirm_access").setViewName("authorize");
    }

    @Configuration
    @Order(2)
    protected static class LoginConfig extends WebSecurityConfigurerAdapter implements ApplicationEventPublisherAware {

        @Autowired
        UserDetailsService userDetailsService

        @Autowired
        PasswordEncoder passwordEncoder

        ApplicationEventPublisher applicationEventPublisher


        @Bean
        DaoAuthenticationProvider daoAuthenticationProvider() {
            DaoAuthenticationProvider provider = new DaoAuthenticationProvider()
            provider.passwordEncoder = passwordEncoder
            provider.userDetailsService = userDetailsService
            return provider
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.parentAuthenticationManager(authenticationManagerBean())
                    .userDetailsService(userDetailsService)
                    .passwordEncoder(passwordEncoder())
        }

        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            //return super.authenticationManagerBean()
            ProviderManager providerManager = new ProviderManager([daoAuthenticationProvider()], super.authenticationManagerBean())
            providerManager.setAuthenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationEventPublisher))
            return providerManager
        }

        @Bean
        public PasswordEncoder passwordEncoder() {
            new BCryptPasswordEncoder(5)
        }
    }


    @Configuration
    @EnableResourceServer
    protected static class ResourceServer extends ResourceServerConfigurerAdapter {

        @Value('${oauth.resourceId}')
        private String resourceId

        @Autowired
        @Qualifier('authenticationManagerBean')
        private AuthenticationManager authenticationManager

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.setSharedObject(AuthenticationManager.class, authenticationManager)

            http.csrf().disable()
            http.httpBasic().disable()

            http.formLogin().loginPage("/login").permitAll()

            //http.authenticationProvider(daoAuthenticationProvider())

            http.anonymous().and()
                    .authorizeRequests()
                    .antMatchers('/login/**').permitAll()
                    .antMatchers('/uaa/register/**').permitAll()
                    .antMatchers('/uaa/activate/**').permitAll()
                    .antMatchers('/uaa/password/**').permitAll()
                    .antMatchers('/uaa/account/**').hasAuthority('ADMIN')
                    .antMatchers('/api-docs/**').permitAll()
                    .antMatchers('/admin/**').hasAuthority('SUPERADMIN')
                    .anyRequest().authenticated()

            //http.sessionManagement().sessionCreationPolicy(STATELESS)
        }

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.resourceId(resourceId)
            resources.authenticationManager(authenticationManager)
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

        @Value('${oauth.clientId}')
        private String clientId

        @Value('${oauth.secret:}')
        private String secret

        @Value('${oauth.resourceId}')
        private String resourceId

        @Autowired
        @Qualifier('authenticationManagerBean')
        private AuthenticationManager authenticationManager

        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
            return new JwtAccessTokenConverter();
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.checkTokenAccess("permitAll()")
            oauthServer.allowFormAuthenticationForClients()
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager)
                    .accessTokenConverter(accessTokenConverter())
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient(clientId)
                    .secret(secret)
                    .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
                    .authorities("USER", "ADMIN")
                    .scopes("read", "write", "trust")
                    .resourceIds(resourceId)
        }
    }
}

主要问题是我不能使两者(web登录表单和头部的OAuth2授权令牌)运行。如果ResourceServer获得更高的优先级,那么OAuth2令牌授权就可以工作,但是我不能使用web表单登录。另一方面,如果我将更高的优先级设置为LoginConfig类,那么OAuth2令牌授权将停止工作。

我发现,在这种情况下,问题是由未注册的OAuth2AuthenticationProcessingFilter引起的。我试图在ResourceServer.configure(HttpSecurity http)方法中手动注册它,但它不起作用--我可以在FilterChain列表中看到过滤器,但它没有被触发。这不是修复它的好方法,因为在ResourceServer初始化期间有很多其他的魔法,所以我转到第二种情况。

在这种情况下,主要问题是默认情况下UsernamePasswordAuthenticationFilter找不到正确配置的AuthenticationProvider实例(在ProviderManager中)。当我尝试通过以下方式手动添加它时:

http.authenticationProvide(daoAuthenticationProvider())

它得到一个,但是在本例中没有定义AuthenticationEventPublisher,并且成功的身份验证不能发布到其他组件。事实上,在下一次迭代中,它将被AnonymousAuthenticationToken所取代。这就是为什么我试图手动定义AuthenticationManager实例,其中包含DAOAuthenticationProvider

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    //return super.authenticationManagerBean()
    ProviderManager providerManager = new ProviderManager([daoAuthenticationProvider()], super.authenticationManagerBean())
    providerManager.setAuthenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationEventPublisher))
    return providerManager
}

我认为它会起作用,但是在向已注册的筛选器提供AuthenticationManager实例时有一个不同的问题。事实证明,每个筛选器都使用SharedObjects组件手动注入AuthenticationManager:

authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));

这里的问题是,您不能保证有一个正确的实例集,因为有一个简单的HashMap(在GitHub上查看)用于存储特定的共享对象,并且可以随时更改。我试着把它放进去:

http.setSharedObject(AuthenticationManager.class, authenticationManager)

但是在我到达读取它的地方之前,它已经被默认实现替换了。我用调试器检查了它,看起来每个新筛选器都有一个新的authentication Manager实例。

我的问题是:我做得对吗?如何设置授权服务器,将资源服务器集成到一个应用程序中,并使用登录表单(OAuth2对话框)工作?也许可以用一种不同的、容易得多的方式来完成。如果有任何帮助,我将不胜感激。

共有1个答案

万修为
2023-03-14

这是解决问题的办法。看一下这个示例性的Groovy类:

@Configuration
@EnableResourceServer
class ResourceServer extends ResourceServerConfigurerAdapter {

    @Value('${oauth.resourceId}')
    private String resourceId

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
        http.httpBasic().disable()

        http.requestMatchers().antMatchers('/admin/**', '/uaa/**')
                .and().authorizeRequests()
                    .antMatchers('/uaa/authenticated/**').authenticated()
                    .antMatchers('/uaa/register/**').permitAll()
                    .antMatchers('/uaa/activate/**').permitAll()
                    .antMatchers('/uaa/password/**').permitAll()
                    .antMatchers('/uaa/auth/**').permitAll()
                    .antMatchers('/uaa/account/**').hasAuthority('ADMIN')
                    .antMatchers('/admin/**').hasAuthority('ADMIN')
                    .anyRequest().authenticated()

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId(resourceId);
    }
}

基本上,要将OAuth2.0身份验证与web表单身份验证并行运行,您必须将

http.requestMatchers().antMatchers('/path/1/**', '/path/2/**')

设置为配置类。我以前的配置忽略了这一重要部分,因此只有OAuth2.0参与了身份验证过程。

 类似资料:
  • 我正在使用Spring Boot,我希望我的应用程序托管OAuth2资源服务器,以便在同一服务器上访问我的apiendpoint。我还需要有一个网页界面,通过表格登录的安全页面。 例如,我有apiendpoint/api/v1/**,在这里只能通过从oauth2资源服务器获得令牌来发出请求。 此外,还有像/account/**这样的endpoint,用户需要通过表单登录。 所有这些现在都需要放在一

  • 问题内容: 随着Spring Security 4的发布及其对测试的增强支持,我想更新我当前的Spring Security oauth2资源服务器测试。 目前,我有一个帮助程序类,用于建立与测试连接到实际应用程序的使用,以为我的测试请求有效令牌。然后,此resttemplate用于在s中发出请求。 我想利用Spring Security 4中的新测试支持,放弃对实际AuthorizationSe

  • 在Spring Security 4发布之后,我想更新我当前的Spring Security oauth2资源服务器测试。 目前,我有一个helper类,它使用设置,并使用一个test连接到一个实际的来为我的测试请求一个有效的令牌。然后使用此resttemplate在我的中发出请求。 通过利用Spring Security4中的新测试支持,我希望在测试中放弃对实际AuthorizationServ

  • 我正在学习Spring Boot Security入门第五部分,以保护我的RESTful微服务。 我打算实现的简单流程是:- > 如果未经身份验证,用户将被重定向到一个自定义登录页,地址是'/login'。 用户提供他的凭据。 上面提到的链接中的入门指南使用了在。properties或。yml文件中配置的基本Auth和虚拟用户。 这是我尝试配置的方式:- 单击授权endpoint会将我重定向到“h

  • 我是Spring安全 4 的新手。我已经用支柱 2 和Spring安全性做了一个简单的应用程序。我正在使用自定义登录表单。发生的事情是当我提交登录表单时,我被重定向到 http://localhost:8080/SpringSecurity/admin 但仍然收到以下错误 HTTP状态404- /SpringSecurity/admin 类型状态报告 message/SpringSecurity/

  • 我正在尝试使用Spring构建一个OAuth2授权服务器。问题是我无法让它与登录和授权表单以及资源服务器一起使用oAuth2令牌检索用户数据。 这是我的主要配置,除了用户服务和存储库... 网络安全配置 授权服务器配置 资源服务器配置 用户控制器 正如你所看到的,我给他们下了订单。问题是,如果WebSecurityConfig是第一个,我可以进入/login和/oauth/authorize屏幕,