SpringBoot使用JWT进行跨域身份验证
整合JWT令牌
一、不在拦截器使用
1、在模块中添加jwt工具依赖
<dependencies> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> </dependency> </dependencies>
package com.stu.service.base.utils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /****************************** * 用途说明: * 作者姓名: Administrator * 创建时间: 2022-06-30 21:47 ******************************/ @Data @AllArgsConstructor @NoArgsConstructor public class JwtInfo { private String id; private String nickName; private String avatar; //权限、角色等 //不要存敏感信息 }
JwtUtils
package com.stu.service.base.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.joda.time.DateTime; import org.springframework.util.StringUtils; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import javax.xml.bind.DatatypeConverter; import java.security.Key; import java.util.Date; /****************************** * 用途说明:jwt工具类 * 作者姓名: Administrator * 创建时间: 2022-06-30 21:48 ******************************/ public class JwtUtils { public static final String APP_SECRET = "sdfGRRD323FGSfdrtr4233"; private static Key getKeyInstance() { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; byte[] bytes = DatatypeConverter.parseBase64Binary(APP_SECRET); return new SecretKeySpec(bytes, signatureAlgorithm.getJcaName()); } /** * 获取 jwt token * * @param jwtInfo 实体 * @param expire 过期时间 m * @return 生成的 token */ public static String getJwtToken(JwtInfo jwtInfo, int expire) { String JwtToken = Jwts.builder() .setHeaderParam("typ", "JWT") .setHeaderParam("alg", "HS256") // 主题 .setSubject("stu-user") // 颁发时间 .setIssuedAt(new Date()) // 过期时间 .setExpiration(DateTime.now().plusSeconds(expire).toDate()) // 用户id .claim("id", jwtInfo.getId()) // 用户昵称 .claim("nickName", jwtInfo.getNickName()) // 用户头像 .claim("avatar", jwtInfo.getAvatar()) .signWith(SignatureAlgorithm.HS256, getKeyInstance()) .compact(); return JwtToken; } /** * 判断token是否存在与有效 * * @param jwtToken * @return */ public static boolean checkJwtToken(String jwtToken) { if (StringUtils.isEmpty(jwtToken)) { return false; } try { Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwtToken); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 判断token是否存在与有效 * * @param request * @return */ public static boolean checkJwtToken(HttpServletRequest request) { String jwtToken = request.getHeader("token"); return checkJwtToken(jwtToken); } /** * 根据token获取会员信息 * * @param request * @return */ public static JwtInfo getMemberIdByJwtToken(HttpServletRequest request) { String jwtToken = request.getHeader("token"); if (StringUtils.isEmpty(jwtToken)) { return null; } Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwtToken); Claims claims = claimsJws.getBody(); JwtInfo jwtInfo = new JwtInfo(claims.get("id").toString(), claims.get("nickName").toString(), claims.get( "avatar").toString()); return jwtInfo; } }
3、使用
package com.stu.service.trade.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.stu.service.base.dto.CourseDto; import com.stu.service.base.dto.MemberDto; import com.stu.service.base.exception.CustomException; import com.stu.service.base.result.R; import com.stu.service.base.result.ResultCodeEnum; import com.stu.service.base.utils.JwtInfo; import com.stu.service.base.utils.JwtUtils; import com.stu.service.trade.entity.Order; import com.stu.service.trade.feign.EduCourseService; import com.stu.service.trade.feign.UcenctMemberService; import com.stu.service.trade.mapper.OrderMapper; import com.stu.service.trade.service.OrderService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.stu.service.trade.utli.OrderUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; /** * <p> * 订单 服务实现类 * </p> * * @author stu * @since 2022-07-10 */ @Service public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService { @Autowired private EduCourseService eduCourseService; @Autowired private UcenctMemberService ucenctMemberService; /*********************************** * 用途说明:新增订单 * @param courseId * @param request * 返回值说明: * @return java.lang.String ***********************************/ @Override public String saveOrder(String courseId, HttpServletRequest request) { Order addOrder = null; JwtInfo jwtInfo = JwtUtils.getMemberIdByJwtToken(request); if (null == jwtInfo) { throw new CustomException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD); } String memberId = jwtInfo.getId(); if (StringUtils.isEmpty(memberId)) { throw new CustomException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD); } //查询当前用户是否已有当前课程的订单 QueryWrapper<Order> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("course_id", courseId); queryWrapper.eq("member_id", memberId); Order order = baseMapper.selectOne(queryWrapper); if (null != order) { return order.getId(); } //查询用户信息 MemberDto memberDto = ucenctMemberService.getMemberInfo(memberId); if (null == memberDto) { throw new CustomException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR); } //查询课程信息 CourseDto courseDto = eduCourseService.getCourseById(courseId); if (null == courseDto) { throw new CustomException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR); } addOrder = new Order(); addOrder.setOrderNo(OrderUtils.getOrderNo()); addOrder.setCourseId(courseId); addOrder.setCourseId(courseDto.getId()); addOrder.setCourseTitle(courseDto.getTitle()); addOrder.setCourseCover(courseDto.getCover()); addOrder.setTotalFee(courseDto.getPrice()); addOrder.setTeacherName(courseDto.getTeacherName()); addOrder.setMemberId(memberId); addOrder.setNickname(jwtInfo.getNickName()); addOrder.setMobile(memberDto.getMobile()); addOrder.setStatus(0);//"订单状态(0:未支付 1:已支付)" addOrder.setPayType(1); baseMapper.insert(addOrder); return addOrder.getId(); } /*********************************** * 用途说明:支付成功 * @param orderId * 返回值说明: * @return boolean ***********************************/ @Override public Order updateOrderStraus(String orderId) { Order order = baseMapper.selectById(orderId); order.setStatus(1); eduCourseService.updateBuyCountById(order.getCourseId()); return order; } }
4、vue页面
import axios from 'axios' import { Message, MessageBox } from 'element-ui' import store from '../store' import { getToken } from '@/utils/auth' // 创建axios实例 const service = axios.create({ baseURL: process.env.BASE_API, // api的base_url timeout: 5000 // 请求超时时间 }) // request拦截器 service.interceptors.request.use( config => { if (store.getters.token) { config.headers['token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 } return config }, error => { // Do something with request error console.log(error) // for debug Promise.reject(error) } ) // respone拦截器 service.interceptors.response.use( response => { /** * code为非20000是抛错 可结合自己业务进行修改 */ const res = response.data if (res.code !== 20000) { Message({ message: res.message, type: 'error', duration: 5 * 1000 }) // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; if (res.code === 50008 || res.code === 50012 || res.code === 50014) { MessageBox.confirm( '你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' } ).then(() => { store.dispatch('FedLogOut').then(() => { location.reload() // 为了重新实例化vue-router对象 避免bug }) }) } return Promise.reject('error') } else { return response.data } }, error => { console.log('err' + error) // for debug Message({ message: error.message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } ) export default service
二、通过拦截器方式验证
创建拦截器
package com.qfedu.fmmall.interceptor; import com.fasterxml.jackson.databind.ObjectMapper; import com.qfedu.fmmall.vo.ResStatus; import com.qfedu.fmmall.vo.ResultVO; import io.jsonwebtoken.*; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @Component public class CheckTokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String method = request.getMethod(); if("OPTIONS".equalsIgnoreCase(method)){ return true; } String token = request.getHeader("token"); if(token == null){ ResultVO resultVO = new ResultVO(ResStatus.LOGIN_FAIL_NOT, "请先登录!", null); doResponse(response,resultVO); }else{ try { JwtParser parser = Jwts.parser(); parser.setSigningKey("QIANfeng6666"); //解析token的SigningKey必须和生成token时设置密码一致 //如果token正确(密码正确,有效期内)则正常执行,否则抛出异常 Jws<Claims> claimsJws = parser.parseClaimsJws(token); return true; }catch (ExpiredJwtException e){ ResultVO resultVO = new ResultVO(ResStatus.LOGIN_FAIL_OVERDUE, "登录过期,请重新登录!", null); doResponse(response,resultVO); }catch (UnsupportedJwtException e){ ResultVO resultVO = new ResultVO(ResStatus.LOGIN_FAIL_NOT, "Token不合法,请自重!", null); doResponse(response,resultVO); }catch (Exception e){ ResultVO resultVO = new ResultVO(ResStatus.LOGIN_FAIL_NOT, "请先登录!", null); doResponse(response,resultVO); } } return false; } private void doResponse(HttpServletResponse response,ResultVO resultVO) throws IOException { response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); PrintWriter out = response.getWriter(); String s = new ObjectMapper().writeValueAsString(resultVO); out.print(s); out.flush(); out.close(); } }
配置拦截器
package com.qfedu.fmmall.config; import com.qfedu.fmmall.interceptor.CheckTokenInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Autowired private CheckTokenInterceptor checkTokenInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(checkTokenInterceptor) .addPathPatterns("/shopcart/**") .addPathPatterns("/orders/**") .addPathPatterns("/useraddr/**") .addPathPatterns("/user/check"); } }
axios通过请求头传值
axios({ method: "get", url: baseUrl + "shopcart/list", headers: { token: this.token } }).then(function(res) { console.log(res); });
各种异常
JwtException 总异常
ClaimJwtException 获取Claim异常
ExpiredJwtException token过期异常
IncorrectClaimException token无效
MalformedJwtException 密钥验证不一致
MissingClaimException JWT无效
RequiredTypeException 必要类型异常
SignatureException 签名异常
UnsupportedJwtException 不支持JWT异常
作者:明
出处:https://www.cnblogs.com/konglxblog//
版权:本文版权归作者和博客园共有
转载:欢迎转载,文章中请给出原文连接,此文章仅为个人知识学习分享,否则必究法律责任