如何在 Spring Boot OAuth2 的 SecurityContextHolder 中获取 JWT 令牌?
Posted
技术标签:
【中文标题】如何在 Spring Boot OAuth2 的 SecurityContextHolder 中获取 JWT 令牌?【英文标题】:How to get JWT token in SecurityContextHolder in Spring Boot OAuth2? 【发布时间】: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();
【问题讨论】:
【参考方案1】:如果你打算使用 spring-security-oauth2-resource-server 依赖,
我认为最好使用@AuthenticationPrincipal Jwt token
来获取令牌的声明。
这样的代码更简单,样板代码更少。
但这取决于你,Imranmadbar 的解决方案也有效)
【讨论】:
【参考方案2】:如果您想要来自 SecurityContextHolder 的信息,您必须将其保留在那里。
这是最简单的解决方案:
-
从您的当前日志用户信息所在的request获取Auth Token。
使用某些 Util 方法从 jwt 中提取日志 用户名。
使用此用户名从数据库中获取用户详细信息。
最后将此用户信息设置到 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);
【讨论】:
以上是关于如何在 Spring Boot OAuth2 的 SecurityContextHolder 中获取 JWT 令牌?的主要内容,如果未能解决你的问题,请参考以下文章
如何在我的 Spring Boot Security OAuth2 应用程序中仅为某些类启用 OAuth2?
如何在 Spring Boot 中实现 oAuth2 和 JWT 身份验证? [关闭]
如何从 facebook/google/... oauth2 身份验证在 Spring (Boot) 中为单页应用程序派生自定义登录令牌?
如何在 Spring Boot OAuth2 的 SecurityContextHolder 中获取 JWT 令牌?