Loading... ## 准备: ### 前端统一返回对象: ```java package cn.bdmcom.common; import lombok.Data; /** * @code Description * @code author 本当迷 * @code date 2022/10/8-10:42 */ @Data public class Result<T> { //返回信息码 private String code; //返回信息 private String msg; //返回数据 T data; public Result() { } public Result(T data) { this.data = data; } //成功,只返回成功码和信息 public static <T> Result<T> success() { Result<T> result = new Result<>(); result.setCode(ResultCode.SUCCESS.code); result.setMsg(ResultCode.SUCCESS.msg); return result; } //成功,返回成功码、信息和数据 public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(ResultCode.SUCCESS.code); result.setMsg(ResultCode.SUCCESS.msg); result.setData(data); return result; } //失败,返回自己定义的信息码和信息 public static <T> Result<T> error() { Result<T> result = new Result<>(); result.setCode(ResultCode.ERROR.code); result.setMsg(ResultCode.ERROR.msg); return result; } //失败,返回controller层传过来信息码和信息 public static <T> Result<T> error(String code, String msg) { Result<T> result = new Result<>(); result.setCode(code); result.setMsg(msg); return result; } // 失败, 返回controller层传过来信息 public static <T> Result<T> error(String msg) { Result<T> result = new Result<>(); result.code = ResultCode.ERROR.code; result.setMsg(msg); return result; } // 传入失败状态码信息 public static <T> Result<T> error(ResultCode resultCode){ Result<T> result = new Result<>(); result.code = resultCode.code; result.msg = result.msg; return result; } // 传入成功状态码信息 public static <T> Result<T> success(ResultCode resultCode){ Result<T> result = new Result<>(); result.code = resultCode.code; result.msg = result.msg; return result; } public void setCode(String code) { this.code = code; } public void setMsg(String msg) { this.msg = msg; } public void setData(T data) { this.data = data; } @Override public String toString() { return "Result{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + ", data=" + data + '}'; } } ``` ### 状态码: ```java package cn.bdmcom.common; /** * @code Description * @code author 本当迷 * @code date 2022/10/8-11:06 */ public enum ResultCode { /*系统状态码*/ SUCCESS("20000", "成功"), ERROR("-1", "系统异常"), /*用户状态码*/ PARAM_ERROR("1001", "参数异常"), USER_EXIST_ERROR("2001", "用户已存在"), USER_ACCOUNT_ERROR("2002", "账号或密码错误"), USER_NOT_EXIST_ERROR("2003", "未找到用户"), PARAM_LOST_ERROR("2004", "参数缺失"), PARAM_PASSWORD_ERROR("2005", "原密码输入错误"), /*文件状态码*/ FILE_TYPE("3001", "文件类型错误"), /*token状态码*/ TOKEN_ISNULL("4001", "token不能为空"), TOKEN_INVALID("4002", "token无效"), TOKEN_TIMEOUT("4003", "token过期"), TOKEN_Algorithm("4004", "签名算法不一致"), TOKEN_SIGNATURE("4005", "无效签名"), ; public final String code; public final String msg; ResultCode(String code, String msg) { this.code = code; this.msg = msg; } } ``` ### 本地线程: ```java package cn.bdmcom.common; /** * @code Description 本地线程 可用于保存用户数据 * @code author 本当迷 * @code date 2022/9/26-23:14 */ public class ThreadLocal<T> { private static final java.lang.ThreadLocal<Object> threadLocal = new java.lang.ThreadLocal<>(); public static Object getUser(){ return threadLocal.get(); } public static void setUser(Object user){ threadLocal.set(user); } public static void remove(){ threadLocal.remove(); } } ``` ## 整合JWT ### 导入依赖: ```java <!--JWT--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.0.0</version> </dependency> ``` ### yml配置: ```java # token配置 token: jwt: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: ">?N<:{LWPWXX#$%()(#*!()!KL<><MQLMNQNQJQK sdfkjsdrow32234545fdf" # 令牌有效期,单位分钟(默认30分钟) expireTime: 30 ``` ### JWTUtil ```java package cn.bdmcom.utils; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.auth0.jwt.JWTCreator; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; /** * @code Description 登录Token的生成和解析 * @code author 本当迷 * @code date 2022/11/3-12:48 */ @Component public class JwtUtils { /** * token秘钥 */ public static String SECRET = ""; /** * token 过期时间单位 */ public static final int calendarField = Calendar.MINUTE; /** * token 过期时间 */ public static int calendarInterval = 30; @Value("${token.jwt.secret}") public void setSECRET(String SECRET) { JwtUtils.SECRET = SECRET; } @Value("${token.jwt.expireTime}") public void setCalendarInterval(int calendarInterval) { JwtUtils.calendarInterval = calendarInterval; } /** * JWT生成Token.<br/> * <p> * JWT构成: header, payload, signature * * @param map 登录成功后用户信息 */ public static String createToken(Map<String,String> map) { Date iatDate = new Date(); // expire time Calendar nowTime = Calendar.getInstance(); nowTime.add(calendarField, calendarInterval); Date expiresDate = nowTime.getTime(); // header Map Map<String, Object> header = new HashMap<>(); header.put("alg", "HS256"); header.put("typ", "JWT"); // 创建 token // param backups {iss:Service, aud:APP} JWTCreator.Builder builder = JWT.create().withHeader(header); // header map.forEach(builder::withClaim); // payload // 指定token过期签名 和 签名 return builder.withExpiresAt(expiresDate).sign(Algorithm.HMAC256(SECRET)); } /** * 解密token * @param token 传入的token * @return 解密后的结果 */ public static Map<String, Claim> verifyToken(String token) { DecodedJWT jwt = null; try { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); jwt = verifier.verify(token); } catch (Exception e) { // token 校验失败, 抛出Token验证非法异常 e.printStackTrace(); } assert jwt != null; return jwt.getClaims(); } } ``` ### 自定义拦截器: ```java package cn.bdmcom.config; import cn.bdmcom.MyExceptions.TokenException; import cn.bdmcom.common.ResultCode; import cn.bdmcom.common.ThreadLocal; import cn.bdmcom.utils.JwtUtils; import com.auth0.jwt.exceptions.AlgorithmMismatchException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.interfaces.Claim; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; /** * @code Description * @code author 本当迷 * @code date 2022/11/3-12:51 */ @Configuration public class JWTInterceptor implements HandlerInterceptor { @Value("${token.jwt.header}") private String header; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 获取请求头中的token String token = request.getHeader(header); if(token ==null){ throw new TokenException(ResultCode.TOKEN_ISNULL); } try { // 验证token,返回token中的信息 JwtUtils.verifyToken(token); final Map<String, Claim> tokenInfo = JwtUtils.verifyToken(token); final Claim userId = tokenInfo.get("userId"); ThreadLocal.setUser(Integer.parseInt(userId.asString())); return true; }catch (SignatureVerificationException e){ // 无效签名 throw new TokenException(ResultCode.TOKEN_SIGNATURE); } catch (TokenExpiredException e){ // token过期 throw new TokenException(ResultCode.TOKEN_TIMEOUT); }catch (AlgorithmMismatchException e){ // 签名算法不一致 throw new TokenException(ResultCode.TOKEN_Algorithm); }catch (Exception e){ // token无效 throw new TokenException(ResultCode.TOKEN_INVALID); } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { ThreadLocal.remove(); } } ``` ### 注册拦截器: ```java package cn.bdmcom.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @code Description 拦截器 * @code author 本当迷 * @code date 2022/10/14-23:24 */ @Configuration //一定要加上这个注解,成为Springboot的配置类,不然不会生效 public class WebMvcConfiguration implements WebMvcConfigurer { @Bean public JWTInterceptor jwtInterceptor(){ return new JWTInterceptor(); } @Override //拦截器配置 public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor()) //拦截器注册对象 .addPathPatterns("/**") // 拦截 /user/** .excludePathPatterns("/users/login"); // 不拦截 /user/login } } ``` ### UserController ```java package cn.bdmcom.controller; import cn.bdmcom.common.Result; import cn.bdmcom.common.ThreadLocal; import cn.bdmcom.service.UsersService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @code Description 处理用户接口 * @code author 本当迷 * @code date 2022/10/31-13:05 */ @RequestMapping("users") @RestController public class UsersController { @Resource UsersService usersService; @GetMapping("selectAllUsers") Result<String> selectAllUsers(){ final Integer userId = (Integer) ThreadLocal.getUser(); // 获取通过本地线程获取用户id System.out.println(userId); return Result.success("OK"); } @GetMapping("login") private Result<String> login(String username, String password){ return usersService.login(username, password); } } ``` ### usersServiceImpl ```java package cn.bdmcom.service.impl; import cn.bdmcom.MyExceptions.ServiceException; import cn.bdmcom.common.Result; import cn.bdmcom.common.ResultCode; import cn.bdmcom.utils.JwtUtils; import cn.bdmcom.utils.PasswordEncoder; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import cn.bdmcom.entity.Users; import cn.bdmcom.service.UsersService; import cn.bdmcom.mapper.UsersMapper; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; /** * @author 14740 * @description 针对表【typecho_users】的数据库操作Service实现 * @createDate 2022-10-31 16:09:56 */ @Service public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService{ /** * 登录验证 * @param name 用户名 * @param password 密码 * @return 返回token */ @Override public Result<String> login(String name, String password) { // 如果参数不完整,抛出异常 if(name.isEmpty() || password.isEmpty()) throw new ServiceException(ResultCode.PARAM_LOST_ERROR); final LambdaQueryWrapper<Users> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Users::getName, name); // 通过用户名获取用户全部信息 final Users users = this.getOne(wrapper); // 如果用户不存在, 抛出异常 if(users == null) throw new ServiceException(ResultCode.USER_NOT_EXIST_ERROR); // password与数据库加密后的密码进行对比 final boolean flag = PasswordEncoder.matches(password, users.getPassword()); // 密码错误,抛出异常 if(!flag) throw new ServiceException(ResultCode.PARAM_PASSWORD_ERROR); final Map<String, String> map = new HashMap<>(); map.put("userId", users.getUid().toString()); // 当密码和账号验证成功,生成token,返回前端 final String token = JwtUtils.createToken(map); return Result.success(token); } } ``` ### 效果图: #### 密码正确,成功请求token  #### 携带token请求成功  #### 无token请求失败  #### token无效  ## 自定义全局异常 ### BaseException基础异常类 ```java package cn.bdmcom.MyExceptions; import lombok.Data; /** * @code Description * @code author 本当迷 * @code date 2022/10/31-13:11 */ public class BaseException extends RuntimeException{ private static final long serialVersionUID = 1L; private String code; public BaseException (String code, String msg) { super(msg); this.code = code; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } } ``` ### 自定义异常类继承基础异常类: #### TokenException ```java package cn.bdmcom.MyExceptions; import cn.bdmcom.common.ResultCode; /** * @code Description 自定义token异常类 * @code author 本当迷 * @code date 2022/11/3-13:43 */ public class TokenException extends BaseException{ public TokenException(ResultCode resultCode){ super(resultCode.code, resultCode.msg); } public TokenException(String code, String msg) { super(code, msg); } } ``` #### ServiceException ```java package cn.bdmcom.MyExceptions; import cn.bdmcom.common.ResultCode; /** * @code Description 业务异常处理类 * @code author 本当迷 * @code date 2022/11/3-14:33 */ public class ServiceException extends BaseException{ public ServiceException(ResultCode resultCode){ super(resultCode.code, resultCode.msg); } public ServiceException(String code, String msg) { super(code, msg); } } ``` ### 全局异常捕获 -> 自定义异常: ```java package cn.bdmcom.MyExceptions; import cn.bdmcom.common.Result; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; /** * @code Description 全局异常处理类 * @code author 本当迷 * @code date 2022/10/31-13:12 */ @RestControllerAdvice @Slf4j public class GlobalException { /** * 拦截token异常 * @param tokenException 自定义的token异常类 * @return 返回错误信息 */ @ExceptionHandler(value = TokenException.class) public Result<String> tokenException(TokenException tokenException){ return Result.error(tokenException.getCode(), tokenException.getMessage()); } /** * 拦截业务异常 * @param serviceException 自定义业务异常 * @return 返回错误信息 */ @ExceptionHandler(value = ServiceException.class) public Result<String> serviceException(ServiceException serviceException){ return Result.error(serviceException.getCode(), serviceException.getMessage()); } } ``` 最后修改:2022 年 11 月 03 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 2 如果文章有用,请随意打赏。