【问题标题】:Spring Boot Oauth2 Resource Server UserDetailsServiceSpring Boot Oauth2 资源服务器 UserDetailsS​​ervice
【发布时间】:2021-02-07 08:46:12
【问题描述】:

试图让 UserDetailsS​​ervice 为我设置的 oauth2 资源服务器工作。我能够成功地对 jwt 进行身份验证,但我似乎没有做任何事情来让它调用 loadUserByUsername 方法。这最初是使用 SAML 并且可以正常工作,但现在我已经切换到 Oauth2 并且无法正常工作。

     @Service
     public class OauthUsersDetailsServiceImpl implements UserDetailsService{
         @Override
         public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
             //some user loading junk here - this is never called
         }
     }
     @Configuration
        @EnableGlobalMethodSecurity(prePostEnabled = true)
        @EnableWebSecurity
         public class SecurityConfig extends WebSecurityConfigurerAdapter {
            
            @Override
            protected void configure(HttpSecurity http) throws Exception
            {
                //test key for now
                SecretKeySpec key = new SecretKeySpec("private key0000000000000000000000000000000".getBytes(), "HMACSHA256");
                

                http
                    .authorizeRequests()
                    .antMatchers(/*some endpoints im excluding from auth - this all works*/)
                    .permitAll().and()
                    .authorizeRequests()
                    .anyRequest().authenticated().and()
                    .oauth2ResourceServer().jwt().decoder(NimbusJwtDecoder.withSecretKey(key).build());
            }
         }

我在 google 上发现我可以使用 @service 将类注册为一个 bean,spring 会选择它,但它不起作用。我也尝试通过 AuthenticationManagerBuilder 添加它,但这也不起作用。我的猜测是,它的 jwt 方面有它自己的 UserDetailsS​​ervice ,它已经实现并且优先于我的。也就是说,让我调用的正确方法是什么,或者在身份验证完成后以某种方式手动调用我的用户加载逻辑并覆盖 Principal 对象是否更好?我需要在调用端点之前执行此操作,以便 PreAuthorize 可以检查 UserDetailsS​​ervice 加载的角色。

【问题讨论】:

  • @Mahesh_Loya 不幸的是它没有,我也试过这个。这个问题看起来是针对授权服务器的。我的问题是资源服务器。

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


【解决方案1】:

想通了。希望这将帮助遇到同样问题的任何人。我必须在链中添加一个自定义过滤器来调用我的用户详细信息服务并覆盖上下文:

public class Oauth2AuthorizationFilter extends GenericFilterBean {

        @Autowired
        private OauthUsersDetailsServiceImpl oauthUsersDetailsServiceImpl;
      
      public Oauth2AuthorizationFilter (OauthUsersDetailsServiceImpl userDetailsService) {
        this.oauthUsersDetailsServiceImpl = userDetailsService;
      }
      
      
      @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
          throws IOException, ServletException {

        SecurityContext context = SecurityContextHolder.getContext();
        if(context.getAuthentication() != null && !(context.getAuthentication().getPrincipal() instanceof Users)) {
          
          UserDetails user = oauthUsersDetailsServiceImpl.loadUserByUsername(((Jwt)context.getAuthentication().getPrincipal()).getClaimAsString("user_name")); 
          UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
          context.setAuthentication(authentication);
        }
        
        chain.doFilter(request, response);
      }

    }
@Override
        protected void configure(HttpSecurity http) throws Exception
        {
            //test key for now
            SecretKeySpec key = new SecretKeySpec("private key0000000000000000000000000000000".getBytes(), "HMACSHA256");
            
            http.authorizeRequests().antMatchers(/*bunch of junk...*/).permitAll().and().authorizeRequests().anyRequest().authenticated().and()
                .oauth2ResourceServer().jwt().decoder(NimbusJwtDecoder.withSecretKey(key).build());
            
            http.addFilterAfter(jwtAuthTokenFilterBean(), SwitchUserFilter.class);

        }

这终于满足了我的需要

【讨论】:

    【解决方案2】:

    问题在于JwtAuthenticationProvider 没有调用UserDetailService——它假定JWT 具有所有相关的身份验证信息——所以不需要去UserDetailService 获取权限等。

    因此,您要做的是构建一个 JWT/令牌转换器,从 jwt 中提取用户名并使用 DaoAuthenticationProvider 进行身份验证(这将调用您的 UserDetailsService)。此外,由于密码将为空,因此您必须使用具有 noop additionalAuthenticationChecks 方法的版本覆盖 DaoAuthenticationProvider

    下面是适合我的代码:

    @Configuration
    class OAuthSecurityConfiguration() :
        WebSecurityConfigurerAdapter() {
    
        /*
        Override the default DaoAuthenticationProvider to prevent password validity checks since they will not be set
         */
        @Bean
        fun daoAuthenticationProvider(userDetailsService: UserDetailsService): DaoAuthenticationProvider {
            val daoAuthenticationProvider = object : DaoAuthenticationProvider() {
                override fun additionalAuthenticationChecks(
                    userDetails: UserDetails,
                    authentication: UsernamePasswordAuthenticationToken
                ) {
                    // Do nothing as the password will be set to null
                }
            }
            daoAuthenticationProvider.setUserDetailsService(userDetailsService)
            return daoAuthenticationProvider
        }
    
        override fun configure(http: HttpSecurity) {
            http
                .authorizeRequests()
                .regexMatchers(
                    "/customers.*",
                    "/accounts.*",
                    "/administrators.*"
                )
                .authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt()
                .jwtAuthenticationConverter { jwt ->
                    convertJwtToUsernamePasswordToken(jwt)
                }
        }
    
        private fun convertJwtToUsernamePasswordToken(
            jwt: Jwt
        ): AbstractAuthenticationToken {
            val username = jwt.getClaimAsString("username") // whichever claim you use to transmit the lookup key in the token
            val userPasswordToken = UsernamePasswordAuthenticationToken(username, null)
            return authenticationManager().authenticate(userPasswordToken) as AbstractAuthenticationToken
        }
    }
    

    【讨论】:

      【解决方案3】:

      您需要注册 UserDetailsS​​ervice 实现,然后由 DaoAuthenticationProvider 使用

      // userDetailsService bean
      @Autowired
      private OauthUsersDetailsServiceImpl oauthUsersDetailsServiceImpl;
      
      // 
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.userDetailsService(oauthUsersDetailsServiceImpl);
      }
      

      【讨论】:

      • 也试过了,还是不行。对于它的价值, AuthenticationManagerBuilder 的配置方法肯定会在启动时被调用,所以据我所知,这是有效的。
      猜你喜欢
      • 2017-01-31
      • 2017-10-27
      • 2015-04-07
      • 2018-08-29
      • 2015-05-14
      • 2022-08-03
      • 2017-04-13
      • 2015-08-07
      • 2018-02-13
      相关资源
      最近更新 更多