如何在 Spring Boot OAuth2 的 SecurityContextHolder 中获取 JWT 令牌?

问题描述

我有一个资源服务器,它正在接收带有有效承载令牌的请求。我可以将 @AuthenticationPrincipal Jwt token 用于需要从令牌中获取声明的所有请求,或者我应该能够从 SecurityContextHolder 获取用户信息,对吗?我想从上下文持有者那里得到它。我假设我需要在我的 SecurityConfig 中定义 jwt 转换器?我找不到有关推荐方法的任何信息。

here 中的代码适用于一些旧版本的 Spring Security...我正在使用最新的 Spring Boot 和 Spring security 5.4.2

@Configuration
@EnableWebSecurity
@Order(1)
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
    private String jwkSetUri;

    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .formLogin(AbstractHttpConfigurer::disable)
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeRequests(authorize -> authorize
                        .mvcMatchers(HttpMethod.GET,"/test/**").authenticated()
                        .mvcMatchers(HttpMethod.POST,"/test/**").authenticated()
                )
                .oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
    }

    private JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("entGrps");
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
    }
}

解决方法

如果您打算使用 spring-security-oauth2-resource-server 依赖项, 我认为最好使用 @AuthenticationPrincipal Jwt token 来获取令牌的声明。

这更简单,样板代码更少。

但这取决于您,Imranmadbar 的解决方案也有效)

,

如果您想要来自 SecurityContextHolder 的信息,您必须将其保留在那里。

这是针对此问题的最简单的解决方案

  1. 请求中获取身份验证令牌,其中显示了您的当前日志用户信息
  2. 使用一些Util方法从jwt中提取日志用户名
  3. 使用此用户名从数据库中获取用户详细信息
  4. 最后将此用户信息设置为到 Spring Security 上下文持有者中。

并且您必须使用 HTTP 请求过滤器 (OncePerRequestFilter) 对每个请求执行此操作。

喜欢:

        import java.io.IOException;

        import javax.servlet.FilterChain;
        import javax.servlet.ServletException;
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
        import org.springframework.security.core.context.SecurityContextHolder;
        import org.springframework.security.core.userdetails.UserDetails;
        import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
        import org.springframework.stereotype.Component;
        import org.springframework.web.filter.OncePerRequestFilter;
        import com.madbarsoft.config.MyUserDetailsService;
        import com.madbarsoft.utility.JwtUitl;

        @Component
        public class JwtRequestFilter extends OncePerRequestFilter {    
                    
                    @Autowired
                    private JwtUitl jwtUitl;
                    
                    @Autowired
                    private MyUserDetailsService userDetailsService; 

                    @Override
                    protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chin)
                            throws ServletException,IOException {
                        
                        final String authorizationHeader = request.getHeader("Authorization");
                        System.out.println("Authorization Header #:"+authorizationHeader);
                        
                        String userName = null;
                        String jwtStr = null;
                        
                        if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")){
                            jwtStr = authorizationHeader.substring(7);
                            userName = jwtUitl.extractUserName(jwtStr);
                            System.out.println("Form JWT userName: "+userName);
                        }
                        System.out.println("In Filter Before set #: "+SecurityContextHolder.getContext().getAuthentication());
                        if(userName != null && SecurityContextHolder.getContext().getAuthentication() == null){
                            UserDetails userDetails = this.userDetailsService.loadUserByUsername(userName);
                            if(jwtUitl.validateToken(jwtStr,userDetails)){
                                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                                        userDetails,null,userDetails.getAuthorities());
                                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                            }
                            System.out.println("In Filter After Set #: "+SecurityContextHolder.getContext().getAuthentication());
                        }
                        chin.doFilter(request,response);
                    
                    }
                }

现在您可以从整个项目的任何位置访问用户信息。

(这是一个正在运行的项目,您可以从 gitLink 此处获取一些信息。)