Jwt认证
在许多的项目中,由于做了前后端分离配置,通过jwt生成token,所以需要一个jwt自定义认证过滤器,来实现jwt的token认证;
jwt自定义认证过滤器的作用
JWT自定义认证过滤器的作用是在请求到达后端服务器之前对JWT令牌进行验证和解析,以确保请求的合法性和身份的真实性。
- 验证令牌的有效性:通过检查令牌的签名和过期时间等信息,确保令牌是有效且未过期的。
- 解析令牌获取用户信息:从令牌中提取用户身份信息,如用户名、角色或权限等,以便后续的身份验证和授权操作。
- 进行身份验证:根据令牌中的用户信息,进行用户身份的验证,确保用户是合法的、存在的。
- 设置用户上下文:将验证通过的用户信息存放在上下文中,以便在后续的请求处理过程中使用,如获取当前用户信息、进行权限控制等。
- 过滤请求:根据请求的URL或其他条件,判断是否需要对请求进行身份验证和授权,以决定是否放行请求或返回相应的错误信息。
JWT自定义认证过滤器的实现步骤:
- 创建一个类,命名为JwtAuthenticationFilter,并继承BasicAuthenticationFilter类。
//BasicAuthenticationFilter类是Spring Security框架中的一个过滤器,用于处理基本身份验证。
//它是AbstractAuthenticationProcessingFilter类的子类,用于对请求进行身份验证和授权处理。
- 定义一个URL白名单数组,用于存储需要放行的请求地址。
// 定义一个URL_WHITELIST数组,存储需要放行的请求地址
private static final String URL_WHITELIST[] = {
"/login",
"/logout",
"/captcha",
"/password",
"/image/**"
};
- 创建JwtAuthenticationFilter类的构造函数,传入一个AuthenticationManager对象,用于处理身份验证。
// 定义JwtAuthenticationFilter类的构造函数,传入一个AuthenticationManager对象,该对象用于处理身份验证。
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
- 重写doFilterInternal方法,该方法在每个请求中执行身份验证逻辑。
/**
* 重写doFilterInternal方法,该方法在每个请求中执行,用于身份验证。
* @param request 请求对象
* @param response 响应对象
* @param chain 过滤器链
* @throws IOException
* @throws ServletException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
// 获取请求头中的token
String token = request.getHeader("token");
// 打印请求的URL
System.out.println("请求的url:"+request.getRequestURI());
// 如果token为空或者请求地址在白名单中,则放行
if (StringUtil.isEmpty(token) || new ArrayList<String>(Arrays.asList(URL_WHITELIST)).contains(request.getRequestURI())){
chain.doFilter(request,response);//放行
return;
}
// 验证token是否有效
CheckResult checkResult = JwtUtils.validateJWT(token);//validateJWT时是自定义的验证JWT工具类
if (!checkResult.isSuccess()){//如果验证结果不为空
switch (checkResult.getErrCode()){
// 如果token不存在
case JwtConstant.JWT_ERRCODE_NULL: throw new JwtException("token不存在!");
// 如果token过期
case JwtConstant.JWT_ERRCODE_EXPIRE: throw new JwtException("Token过期");
// 如果验证不通过
case JwtConstant.JWT_ERRCODE_FAIL: throw new JwtException("Token验证不通过");
}
}
// 验证通过,解析token获取用户名
Claims claims = JwtUtils.parseJWT(token);
String username = claims.getSubject();
// 根据用户名获取用户信息
SysUser sysUser = sysUserService.getByUserName(username);
// 将用户信息存放在上下文中
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(username,null,myUserDetailService.getUserAuthority(sysUser.getId()));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
chain.doFilter(request,response);//放行
}
- 在SecurityConfig通过构造函数创建一个JwtAuthenticationFilter对象并返回,该对象用于处理身份验证。
@Bean
JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
JwtAuthenticationFilter jwtAuthenticationFilter=new JwtAuthenticationFilter(authenticationManager());
return jwtAuthenticationFilter;
}
- 将过滤器添加进SecurityConfig中
/**
* 配置应用程序的安全规则
* 开启跨域资源共享(CORS)功能,并关闭跨站请求伪造(CSRF)攻击防护
* 配置登录相关设置
* 禁用会话(session)的创建
* 配置拦截规则
* @param http HttpSecurity对象,用于配置应用程序的安全规则
* @throws Exception 配置过程中可能抛出的异常
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors() // 开启CORS功能(跨域)
.and()
.csrf().disable() // 关闭CSRF攻击防护
.formLogin() // 配置登录相关设置
.successHandler(loginSuccessHandler) // 自定义登录成功处理器
.failureHandler(loginFailureHandler) // 自定义登录失败处理器
// .and()
// .logout() // 配置注销设置(如果需要)
// .logoutSuccessHandler() // 自定义注销成功处理器(如果需要)
.and()
//session禁用配置
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 禁用会话的创建(无状态)
.and()
.authorizeRequests()
.antMatchers(URL_WHITELIST).permitAll() // 白名单中的URL路径允许无需身份验证/permitAll放行所有
.anyRequest().authenticated() // 其他所有请求需要身份验证
//异常处理配置
//自定义过滤器配置
.and()
.addFilter(jwtAuthenticationFilter());//添加filter
}
JwtAuthenticationFilter整体源码
package com.javaandvue.common.security;
import com.javaandvue.common.constant.JwtConstant;
import com.javaandvue.entity.CheckResult;
import com.javaandvue.entity.SysUser;
import com.javaandvue.service.SysUserService;
import com.javaandvue.util.JwtUtils;
import com.javaandvue.util.StringUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
// 创建一个JwtAuthenticationFilter类,继承BasicAuthenticationFilter类
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
// 自动注入SysUserService和MyUserDetailServiceImpl
@Autowired
private SysUserService sysUserService;
@Autowired
private MyUserDetailServiceImpl myUserDetailService;
// 定义一个URL_WHITELIST数组,存储需要放行的请求地址
private static final String URL_WHITELIST[] = {
"/login",
"/logout",
"/captcha",
"/password",
"/image/**"
};
// 定义JwtAuthenticationFilter类的构造函数,传入一个AuthenticationManager对象,该对象用于处理身份验证。
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
/**
* 重写doFilterInternal方法,该方法在每个请求中执行,用于身份验证。
* @param request 请求对象
* @param response 响应对象
* @param chain 过滤器链
* @throws IOException
* @throws ServletException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
// 获取请求头中的token
String token = request.getHeader("token");
// 打印请求的URL
System.out.println("请求的url:"+request.getRequestURI());
// 如果token为空或者请求地址在白名单中,则放行
if (StringUtil.isEmpty(token) || new ArrayList<String>(Arrays.asList(URL_WHITELIST)).contains(request.getRequestURI())){
chain.doFilter(request,response);//放行
return;
}
// 验证token是否有效
CheckResult checkResult = JwtUtils.validateJWT(token);
if (!checkResult.isSuccess()){//如果验证结果不为空
switch (checkResult.getErrCode()){
// 如果token不存在
case JwtConstant.JWT_ERRCODE_NULL: throw new JwtException("token不存在!");
// 如果token过期
case JwtConstant.JWT_ERRCODE_EXPIRE: throw new JwtException("Token过期");
// 如果验证不通过
case JwtConstant.JWT_ERRCODE_FAIL: throw new JwtException("Token验证不通过");
}
}
// 验证通过,解析token获取用户名
Claims claims = JwtUtils.parseJWT(token);
String username = claims.getSubject();
// 根据用户名获取用户信息
SysUser sysUser = sysUserService.getByUserName(username);
// 将用户信息存放在上下文中
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(username,null,myUserDetailService.getUserAuthority(sysUser.getId()));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
chain.doFilter(request,response);//放行
}
}
SecurityConfig整体源码
package com.javaandvue.config;
import com.javaandvue.common.security.JwtAuthenticationFilter;
import com.javaandvue.common.security.LoginFailureHandler;
import com.javaandvue.common.security.LoginSuccessHandler;
import com.javaandvue.common.security.MyUserDetailServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Autowired
private LoginFailureHandler loginFailureHandler;
@Autowired
private MyUserDetailServiceImpl myUserDetailService;
// 定义白名单URL路径数组
private static final String URL_WHITELIST[] = {
"/login",
"/logout",
"/captcha",
"/password",
"/image/**"
};
@Bean
JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
JwtAuthenticationFilter jwtAuthenticationFilter=new JwtAuthenticationFilter(authenticationManager());
return jwtAuthenticationFilter;
}
//默认密码加密配置
@Bean
BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 配置应用程序的安全规则
* 开启跨域资源共享(CORS)功能,并关闭跨站请求伪造(CSRF)攻击防护
* 配置登录相关设置
* 禁用会话(session)的创建
* 配置拦截规则
* @param http HttpSecurity对象,用于配置应用程序的安全规则
* @throws Exception 配置过程中可能抛出的异常
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors() // 开启CORS功能(跨域)
.and()
.csrf().disable() // 关闭CSRF攻击防护
.formLogin() // 配置登录相关设置
.successHandler(loginSuccessHandler) // 自定义登录成功处理器
.failureHandler(loginFailureHandler) // 自定义登录失败处理器
// .and()
// .logout() // 配置注销设置(如果需要)
// .logoutSuccessHandler() // 自定义注销成功处理器(如果需要)
.and()
//session禁用配置
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 禁用会话的创建(无状态)
.and()
.authorizeRequests()
.antMatchers(URL_WHITELIST).permitAll() // 白名单中的URL路径允许无需身份验证/permitAll放行所有
.anyRequest().authenticated() // 其他所有请求需要身份验证
//异常处理配置
//自定义过滤器配置
.and()
.addFilter(jwtAuthenticationFilter());//添加filter
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailService);
}
}
效果演示:
当请求不带token或者Token异常时
当请求携带的Token正常时