📅  最后修改于: 2023-12-03 15:38:26.659000             🧑  作者: Mango
在 Spring Boot 中使用 JWT(Json Web Token)是非常常见的,JWT 用于进行身份验证和授权。在一些特定场景下,可能需要为不同的用户设置不同的身份验证表,在这个过程中需要使用不同的 JWT 令牌,例如管理员用户和非管理员用户需要使用不同的 JWT 令牌才能完成身份验证和权限控制。
下面是为在 Spring Boot 中为单独的 JWT 令牌设置两个不同的表管理员和用户的步骤说明:
在 pom.xml
中添加以下依赖项:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
在 Spring Boot 的配置文件中,按照以下格式进行配置:
jwt:
# token过期时间,默认为1小时
expire-time: 3600000
# token在请求头中的名称,默认为Authorization
token-header: Authorization
# token前缀,例如Bearer+空格,就是Bearer这个前缀加上一个空格,如果没有则为空
token-prefix: Bearer
在 JwtUtil.java
文件中编写 JWT 工具类,代码如下:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.crypto.SecretKey;
import java.util.Date;
@Configuration
public class JwtUtil {
@Autowired
private JwtConfig jwtConfig;
// 生成JWT token
public String generateToken(long userId, String email, boolean isAdmin) {
Date now = new Date();
SecretKey key = Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes());
String token = Jwts.builder()
.signWith(key, SignatureAlgorithm.HS256)
.claim("user_id", userId)
.claim("email", email)
.claim("is_admin", isAdmin)
.setIssuedAt(now)
.setExpiration(new Date(now.getTime() + jwtConfig.getExpireTime()))
.compact();
return jwtConfig.getTokenPrefix() + token;
}
// 解析JWT token
public Claims parseToken(String token) {
SecretKey key = Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes());
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token.replace(jwtConfig.getTokenPrefix(), ""))
.getBody();
}
}
在 AdminAuthenticationFilter.java
文件中编写管理员身份验证过滤器,代码如下:
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class AdminAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@Autowired
private JwtUtil jwtUtil;
public AdminAuthenticationFilter(AuthenticationManager authenticationManager) {
super("/admin/**");
setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String token = getTokenFromRequestHeader(request);
if (StringUtils.isBlank(token)) {
log.error("Admin token is blank");
throw new AdminAuthenticationException("Unauthorized");
}
Claims claims = jwtUtil.parseToken(token);
if (claims == null || !claims.get("is_admin", Boolean.class)) {
log.error("Admin authentication fail");
throw new AdminAuthenticationException("Unauthorized");
}
// 将身份验证信息传递给 AuthenticationManager 并进行身份验证
AdminAuthenticationToken authRequest = new AdminAuthenticationToken(claims.get("user_id", Long.class), token);
authRequest.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return getAuthenticationManager().authenticate(authRequest);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
// 验证成功后,将 Authentication 存储在 SecurityContextHolder 中
SecurityContextHolder.getContext().setAuthentication(authResult);
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new AuthenticationSuccessEvent(authResult));
}
chain.doFilter(request, response);
}
private String getTokenFromRequestHeader(HttpServletRequest request) {
String authHeader = request.getHeader(jwtUtil.getJwtConfig().getTokenHeader());
if (StringUtils.isNotBlank(authHeader) && authHeader.startsWith(jwtUtil.getJwtConfig().getTokenPrefix())) {
return authHeader.replace(jwtUtil.getJwtConfig().getTokenPrefix(), "");
}
return null;
}
}
在 UserAuthenticationFilter.java
文件中编写用户身份验证过滤器,代码如下:
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class UserAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@Autowired
private JwtUtil jwtUtil;
public UserAuthenticationFilter(AuthenticationManager authenticationManager) {
super("/user/**");
setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String token = getTokenFromRequestHeader(request);
if (StringUtils.isBlank(token)) {
log.error("User token is blank");
throw new UserAuthenticationException("Unauthorized");
}
Claims claims = jwtUtil.parseToken(token);
if (claims == null || claims.get("is_admin", Boolean.class)) {
log.error("User authentication fail");
throw new UserAuthenticationException("Unauthorized");
}
// 将身份验证信息传递给 AuthenticationManager 并进行身份验证
UserAuthenticationToken authRequest = new UserAuthenticationToken(claims.get("user_id", Long.class), token);
authRequest.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return getAuthenticationManager().authenticate(authRequest);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
// 验证成功后,将 Authentication 存储在 SecurityContextHolder 中
SecurityContextHolder.getContext().setAuthentication(authResult);
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new AuthenticationSuccessEvent(authResult));
}
chain.doFilter(request, response);
}
private String getTokenFromRequestHeader(HttpServletRequest request) {
String authHeader = request.getHeader(jwtUtil.getJwtConfig().getTokenHeader());
if (StringUtils.isNotBlank(authHeader) && authHeader.startsWith(jwtUtil.getJwtConfig().getTokenPrefix())) {
return authHeader.replace(jwtUtil.getJwtConfig().getTokenPrefix(), "");
}
return null;
}
}
在 AdminAuthenticationToken.java
和 UserAuthenticationToken.java
文件中编写管理员和用户的身份验证 Token 类,代码如下:
AdminAuthenticationToken.java
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
public class AdminAuthenticationToken extends UsernamePasswordAuthenticationToken {
private String token;
public AdminAuthenticationToken(long userId, String token) {
super(userId, null);
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
UserAuthenticationToken.java
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
public class UserAuthenticationToken extends UsernamePasswordAuthenticationToken {
private String token;
public UserAuthenticationToken(long userId, String token) {
super(userId, null);
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
在 AdminAuthenticationException.java
和 UserAuthenticationException.java
文件中编写管理员和用户的身份验证异常类,代码如下:
AdminAuthenticationException.java
import org.springframework.security.core.AuthenticationException;
public class AdminAuthenticationException extends AuthenticationException {
public AdminAuthenticationException(String message) {
super(message);
}
public AdminAuthenticationException(String message, Throwable cause) {
super(message, cause);
}
}
UserAuthenticationException.java
import org.springframework.security.core.AuthenticationException;
public class UserAuthenticationException extends AuthenticationException {
public UserAuthenticationException(String message) {
super(message);
}
public UserAuthenticationException(String message, Throwable cause) {
super(message, cause);
}
}
注意:这两个异常类必须继承自 AuthenticationException
类。
在 SecurityConfig.java
文件中配置 Spring Security,代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 用户身份验证
http.addFilter(new UserAuthenticationFilter(authenticationManager())).authorizeRequests().antMatchers("/user/**").authenticated();
// 管理员身份验证
http.addFilter(new AdminAuthenticationFilter(authenticationManager())).authorizeRequests().antMatchers("/admin/**").authenticated();
http.httpBasic().disable()
.csrf().disable()
.formLogin().disable()
.logout().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
以上配置将 /user/**
和 /admin/**
下的请求分别交给 UserAuthenticationFilter
和 AdminAuthenticationFilter
进行处理,同时在 configure(AuthenticationManagerBuilder auth)
方法中设置了加密方式为 BCryptPasswordEncoder。
本文介绍了如何在 Spring Boot 中为单独的 JWT 令牌设置两个不同的表管理员和用户。关键在于分别编写管理员和用户的身份验证过滤器,并分别设置验证处理类和异常类,最后结合 Spring Security 进行认证和授权管理。