Java 带有JWT令牌的Spring安全性在每个请求上加载Spring UserDetails对象
我正在学习使用JWT令牌和spring引导的spring安全性。我已经正确地执行了它,它工作得很好。但我对JwtRequestFilter的工作原理有一个疑问。为了了解SpringBoot的SpringSecurity,我浏览了几个网站,发现了同样的东西。让我来谈谈主要的疑问。 我在下面添加JwtRequestFilter文件 JwtRequestFilter.javaJava 带有JWT令牌的Spring安全性在每个请求上加载Spring UserDetails对象,java,spring,spring-boot,spring-security,jwt,Java,Spring,Spring Boot,Spring Security,Jwt,我正在学习使用JWT令牌和spring引导的spring安全性。我已经正确地执行了它,它工作得很好。但我对JwtRequestFilter的工作原理有一个疑问。为了了解SpringBoot的SpringSecurity,我浏览了几个网站,发现了同样的东西。让我来谈谈主要的疑问。 我在下面添加JwtRequestFilter文件 JwtRequestFilter.java @Component public class JwtRequestFilter extends OncePerRequest
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// This below line is calling on every request
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
为了验证令牌,我们必须提供SpringUserDetails对象,我们从jwtUserDetailsService获取SpringUserDetails对象。所以这个过滤器将调用的每个请求,然后令牌验证将执行,我们必须在每个请求上调用jwtUserDetailsService。
我的疑问是在我的JWTUserDetails服务中,我添加了一些验证和用户权限。因此,在jwtUserDetailsService中,对每个请求重复以下步骤
@Service("jwtUserDetailsService")
@Transactional
public class JwtUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Autowired
private IUserService service;
@Autowired
private MessageSource messages;
@Autowired
private RoleRepository roleRepository;
@Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if (user == null) {
return new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName("ROLE_USER"))));
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword(), user.isEnabled(), true, true,
true, getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
private List<String> getPrivileges(Collection<Role> roles) {
List<String> privileges = new ArrayList<>();
List<Privilege> collection = new ArrayList<>();
for (Role role : roles) {
collection.addAll(role.getPrivileges());
}
for (Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
}
@Service(“jwtUserDetailsService”)
@交易的
公共类JwtUserDetailsService实现UserDetailsService{
@自动连线
私有用户存储库用户存储库;
@自动连线
私人服务;
@自动连线
私有消息源消息;
@自动连线
私人角色扮演者角色扮演者;
@凌驾
公共用户详细信息loadUserByUsername(字符串电子邮件)
抛出UsernameNotFoundException{
User=userRepository.findByEmail(电子邮件);
if(user==null){
返回新的org.springframework.security.core.userdetails.User(
“,”,真的,真的,真的,真的,
getAuthorities(Arrays.asList)(
roleRepository.findByName(“角色用户”);
}
返回新的org.springframework.security.core.userdetails.User(
user.getEmail(),user.getPassword(),user.isEnabled(),true,true,
true,getAuthories(user.getRoles());
}
私人收藏
因此,此筛选器将调用的每个请求都将进行令牌验证
执行时,我们必须在每次请求时调用jwtUserDetailsService
这是不正确的,因为您有一个条件if(SecurityContextHolder.getContext().getAuthentication()==null)
因此,在第一次验证令牌时,您需要查询您的用户详细信息服务,获取所有授权并将其设置为安全上下文(您已经在这样做:SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
)
此外,使用JWT auth,您通常甚至不需要访问任何用户详细信息服务,因为理想情况下,所有授权都应该包含在令牌本身中。因此,您需要做的唯一一件事就是验证令牌的签名。一旦用户登录了他的身份验证,您就不需要在登录后再次进行db调用在身份验证过程中,应仅在令牌中设置角色的情况下检查y请求用户的授权,您需要验证令牌在每个请求中是否未被篡改
而不是通过从db调用加载用户详细信息来创建用户详细信息
UserDetails UserDetails=this.jwtUserDetailsService.loadUserByUsername(用户名)
您还可以在JWT声明中对用户的用户名和角色进行编码
并通过解析JWT中的声明来创建UserDetails对象。既然spring security中已经有JWT筛选器,为什么还要实现自己的筛选器5@ThomasAndolf,很抱歉,我刚刚开始学习这个。所以我不知道spring security 5提供了这样的功能。你能提供任何链接让我通过吗很难理解这个概念。谢谢。太多的人在谷歌上搜索和阅读旧的教程。总是先看他们自己的文档。它真的很好@ThomasAndolf,谢谢我会研究它。@ThomasAndolf至少他们在谷歌上搜索:-)。我在看文档,但我在列表中没有看到JWT过滤器(我见过有人扩展UsernamePasswordAuthenticationFilter或OncePerFilter)。我在OAuth下看到一个令牌过滤器,但他没有在上面实现OAuth。我同意你的观点,在每个请求上它不应该调用jwtUserDetailsService。在这里,我的前端是有角度的,所以每次我得到请求SecurityContextHolder.getContext()时.getAuthentication()始终为空。从angular开始,我在头中传递令牌。我需要传递任何其他参数吗?据我所知,将在spring security之前调用筛选器,因此我们无法从SecurityContextHolder获取用户。因为我们将如何从spring security context holder中标识哪个用户身份验证对象。请告诉我如果我错了。您可能注册了错误的筛选器。是否可以添加SecurityConfig
code(您实际将此筛选器添加到筛选器链的位置)?