【问题标题】:How to get JWT token in SecurityContextHolder in Spring Boot OAuth2?如何在 Spring Boot OAuth2 的 SecurityContextHolder 中获取 JWT 令牌?
【发布时间】:2021-04-30 00:09:58
【问题描述】:

我有一个资源服务器正在接收带有有效承载令牌的请求。我可以将@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-boot spring-security spring-oauth2


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

      这是最简单的解决方案

      1. 从您的当前日志用户信息所在的request获取Auth Token
      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 获取一些信息。)

      【讨论】:

        【解决方案3】:

        如果我们唯一关心的是从标头中检索令牌,我想出的最简单的解决方案是使用 DefaultBearerTokenResolver:

        public class CheckAuthTokenFilter extends GenericFilterBean {
        
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
                String token = new DefaultBearerTokenResolver().resolve((HttpServletRequest) servletRequest);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2019-05-12
          • 2020-04-23
          • 1970-01-01
          • 2018-08-03
          • 2017-06-02
          • 2015-04-16
          • 2017-01-01
          • 2021-10-13
          • 2021-08-11
          相关资源
          最近更新 更多