spring jwt认证,springboot实现注册登录功能

  spring jwt认证,springboot实现注册登录功能

  

目录

一、实现流程1.注册2.登录3.登录保持【状态续签】二、实现方法1.引入依赖2 .应用配置文件3.mysql建表4.豆三、测试1.注册2.登录3.状态续签【登录保持】

 

  

一、实现流程

 

  

1.注册

 

  

2.登录

 

  

3.登录保持【状态续签】

 

  

二、实现方法

项目结构

 

  

1.引入依赖

!-spring-web-dependency groupIdorg.springframework.boot/groupId artifact id spring-boot-starter-web/artifact id/dependency!- mysql连接器-dependency groupId MySQL/groupId artifact id MySQL-connector-Java/artifact id/dependency!-我的batis Plus-dependency groupIdcom.baomidou/groupId artifactId我的batis-Plus-boot-starter/artifactId版本3.4.3.4/version/dependency!-德鲁伊-依赖groupIdcom.alibaba/groupId工艺品Id德鲁伊-弹簧靴启动器/artifactId版本1 . 2 . 8/版本/依赖关系!-jwt-dependency groupId com。auth 0/groupId artifact id Java-jwt/artifact id版本3 . 10 . 3/版本/依赖性

 

  

2.application配置文件

春季:应用程序:名称: jwtLogin #应用名称数据源:德鲁伊:网址: JDBC : MySQL ://192。168 .0 .11:3306/登录_测试?使用SSL=false服务器时区=UTC用户名: samon密码: 123456驱动程序类名称: com。MySQL。JDBC。驱动服务器:端口: 8848 #应用服务网访问端口

 

  

3.mysql建表

用户表

 

  

4.Bean

1.bean/user.java用户豆

 

  包com。CX明星报。豆;导入com。窦米宝。mybatisplus。注释。id类型;导入com。窦米宝。mybatisplus。安诺塔

  tion.TableId;import lombok.Data;import java.util.Date;@Datapublic class User { // 自增长id @TableId(type = IdType.AUTO) private Integer id; private String userName; private String passWord; private Date createTime; private Date lastLogin; public User() {} public User(Integer id, String userName) { this.id = id; this.userName = userName; }}2.bean/ServiceRes.java统一Service返回类

  

package com.cxstar.bean;import lombok.Data;@Datapublic class ServiceRes { private Integer code; private String msg; private String jwt; private ServiceRes() {} public ServiceRes(Integer code, String msg) { this.code = code; this.msg = msg; } public ServiceRes(Integer code, String msg, String jwt) { this.code = code; this.msg = msg; this.jwt = jwt; }}

3.bean/ControllerRes.java统一Controller返回类

 

  

package com.cxstar.bean;import lombok.Data;@Datapublic class ControllerRes { private Integer code; private String msg; public ControllerRes(Integer code, String msg) { this.code = code; this.msg = msg; }}

5.Mapper mapper/UserMapper.java继承MP

 

  

package com.cxstar.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.cxstar.bean.User;import org.apache.ibatis.annotations.Mapper;@Mapperpublic interface UserMapper extends BaseMapper<User> {}

6.service service.userService.interface登录、注册、改密业务【状态续签测试】

 

  

package com.cxstar.service;import com.cxstar.bean.ServiceRes;import com.cxstar.bean.User;public interface userService { // 注册 ServiceRes register(User user); // 登录 ServiceRes login(User user); // 改密【带权限业务,用于状态续签测试】 ServiceRes changePassWord(User user);}

service.impl.UserServiceImpl.java登录、注册、改密业务【状态续签测试】

 

  

package com.cxstar.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.cxstar.bean.ServiceRes;import com.cxstar.bean.User;import com.cxstar.mapper.UserMapper;import com.cxstar.service.userService;import com.cxstar.utils.JwtUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.util.DigestUtils;import java.nio.charset.StandardCharsets;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;@Slf4j@Servicepublic class UserServiceImpl implements userService { @Autowired UserMapper userMapper; /** * 注册 * @param user 用户类 * @return ServiceRes */ @Override public ServiceRes register(User user) { // 判断用户名是否唯一 if(this.checkUserNameIsUnique(user)) { // 判断用户名密码是否合法 if(this.checkUserNameAndPassword(user)) { // 密码MD5加密 user.setPassWord(this.MD5Code(user.getPassWord())); // 加入创建时间 user.setCreateTime(new Date()); // 入库 userMapper.insert(user); return new ServiceRes(1, "注册成功"); } else return new ServiceRes(-1, "用户名或密码不合法"); } else return new ServiceRes(-1, "用户名已存在"); } /** * 登录 * @param user 用户类 * @return ServiceRes */ @Override public ServiceRes login(User user) { // 判断用户名密码是否合法 if(this.checkUserNameAndPassword(user)) { // 密码MD5加密 user.setPassWord(this.MD5Code(user.getPassWord())); // 检查用户是否存在 User curUser = this.checkUserIsExit(user); if(curUser!=null) { // 更新用户最后登录时间 curUser.setLastLogin(new Date()); userMapper.updateById(curUser); // 生成jwt Map<String, String> payload = new HashMap<>(); payload.put("userId", curUser.getId().toString()); // 加入一些非敏感的用户信息 payload.put("userName", curUser.getUserName()); // 加入一些非敏感的用户信息 String jwt = JwtUtil.generateToken(payload); return new ServiceRes(1, "登录成功", jwt); } else return new ServiceRes(-1, "用户名或密码错误"); } else return new ServiceRes(-1, "用户名或密码不合法"); } /** * 改密业务 * @return ServiceRes */ @Override public ServiceRes changePassWord(User user) { if(this.updatePassWord(user)) return new ServiceRes(1, "改密成功"); else return new ServiceRes(-1, "改密失败"); } /** * 非对称加密 * @param text 明文 * @return 密文 */ private String MD5Code(String text) { return DigestUtils.md5DigestAsHex(text.getBytes(StandardCharsets.UTF_8)); } /** * 修改密码方法 * @param user 传入用户名和新密码 * @return 改密成功返回 true 失败返回 false */ private Boolean updatePassWord(User user) { // 密码非对称加密 user.setPassWord(this.MD5Code(user.getPassWord())); // 更新密码 return userMapper.updateById(user)>0; } /** * 检查用户是否存在【用户名密码相同】 * @param user 用户类 * @return 用户存在返回 用户对象 不存在返回 null */ private User checkUserIsExit(User user) { LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>(); lqw.eq(User::getUserName, user.getUserName()); lqw.eq(User::getPassWord, user.getPassWord()); return userMapper.selectOne(lqw); } /** * 判断用户名是否唯一 * @param user 用户类 * @return 唯一返回 true 不唯一返回 false */ private Boolean checkUserNameIsUnique(User user) { LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>(); lqw.eq(User::getUserName, user.getUserName()); List<User> userList = userMapper.selectList(lqw); return userList.size() == 0; } /** * 判断用户名密码是否合法 * @param user 用户类 * @return 满足 【英文字母、数字、下划线】 返回 true,否则返回 false */ private Boolean checkUserNameAndPassword(User user) { String regex = "^[_a-z0-9A-Z]+$"; return user.getUserName().matches(regex) && user.getPassWord().matches(regex); }}

6.Controller controller/UserController.java登录、注册、改密业务【状态续签测试】

 

  

package com.cxstar.controller;import com.cxstar.bean.ControllerRes;import com.cxstar.bean.ServiceRes;import com.cxstar.bean.User;import com.cxstar.service.userService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Slf4j@Controller@ResponseBody@RequestMapping("/user")public class UserController { @Autowired userService userService; @PostMapping("/register") public ControllerRes register(User user) { // 注册 ServiceRes serviceRes = userService.register(user); return new ControllerRes(serviceRes.getCode(), serviceRes.getMsg()); } @PostMapping("/login") public ControllerRes login(User user, HttpServletResponse response) { // 登录 ServiceRes serviceRes = userService.login(user); // 登录成功后往响应头插入jwt if(serviceRes.getJwt() != null) response.addHeader("access-token", serviceRes.getJwt()); return new ControllerRes(serviceRes.getCode(), serviceRes.getMsg()); } @PutMapping("/pwd") public ControllerRes changePassWord(User user, HttpServletRequest request) { // 取出jwt中的用户 User jwtUser = (User)request.getAttribute("jwt-user"); // 合并jwt中用户的用户名与传入用户的新密码 // 此处不能直接使用传入的用户名,防止恶意修改其他用户的密码 user.setId(jwtUser.getId()); // 改密 ServiceRes serviceRes = userService.changePassWord(user); return new ControllerRes(serviceRes.getCode(), serviceRes.getMsg()); }}

7.JWT工具类 utils/JwtUtil.java生成和解析 token 的方法

 

  

package com.cxstar.utils;import com.auth0.jwt.JWT;import com.auth0.jwt.JWTCreator;import com.auth0.jwt.algorithms.Algorithm;import com.auth0.jwt.interfaces.DecodedJWT;import com.auth0.jwt.interfaces.JWTVerifier;import java.util.Calendar;import java.util.Date;import java.util.Map;public class JwtUtil { // 签名密钥 private static final String SECRET = "hello JWT *%$#$&"; /** * 生成token * @param payload token携带的信息 * @return token字符串 */ public static String generateToken(Map<String,String> payload){ // 指定token过期时间 Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.HOUR, 24); // 24小时 JWTCreator.Builder builder = JWT.create(); // 构建payload payload.forEach(builder::withClaim); // 指定签发时间、过期时间 和 签名算法,并返回token String token = builder.withIssuedAt(new Date()).withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SECRET)); return token; } /** * 解析token * @param token token字符串 * @return 解析后的token类 */ public static DecodedJWT decodeToken(String token){ JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); DecodedJWT decodedJWT = jwtVerifier.verify(token); return decodedJWT; }}

8.HandlerInterceptor拦截器 interceptor.java拦截器业务实现

 

  

package com.cxstar.interceptor;import com.alibaba.fastjson.JSONObject;import com.auth0.jwt.exceptions.JWTDecodeException;import com.auth0.jwt.exceptions.TokenExpiredException;import com.auth0.jwt.interfaces.DecodedJWT;import com.cxstar.bean.ControllerRes;import com.cxstar.bean.User;import com.cxstar.utils.JwtUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.Date;import java.util.HashMap;import java.util.Map;/** * 拦截需要授权的接口 */@Slf4jpublic class PermisssionInterceptor implements HandlerInterceptor { // 目标方法执行前调用 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { // 检查用户JWT String jwt = request.getHeader("access-token"); // 校验并取出私有信息 try { // token 解码 DecodedJWT dj = JwtUtil.decodeToken(jwt); // 取出基本用户信息加入请求头 -------------------------------------------------------------------------------- String userId = dj.getClaim("userId").asString(); String userName = dj.getClaim("userName").asString(); // jwt校验合格的,将 jwt 中存的用户信息加入请求头,不合格的,请求头存个空用户 request.setAttribute("jwt-user", userId!=null?new User(Integer.valueOf(userId), userName):new User()); // ------------------------------------------------------------------------------------------------------- // 计算当前时间是否超过过期时间的一半,如果是就帮用户续签 -------------------------- // 此处并不是永久续签,只是为 大于过期时间的一半 且 小于过期时间 的 token 续签 Long expTime = dj.getExpiresAt().getTime(); Long iatTime = dj.getIssuedAt().getTime(); Long nowTime = new Date().getTime(); if((nowTime-iatTime) > (expTime-iatTime)/2) { // 生成新的jwt Map<String, String> payload = new HashMap<>(); payload.put("userId", userId); // 加入一些非敏感的用户信息 payload.put("userName", userName); // 加入一些非敏感的用户信息 String newJwt = JwtUtil.generateToken(payload); // 加入返回头 response.addHeader("access-token", newJwt); } // ----------------------------------------------------------------------- return true; } catch (JWTDecodeException e) { log.error("令牌错误"); addResBody(response, new ControllerRes(-1, "令牌错误")); // 新增返回体 return false; } catch (TokenExpiredException e) { log.error("令牌过期"); addResBody(response, new ControllerRes(-1, "令牌过期")); // 新增返回体 return false; } } // 目标方法执行后调用 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } // 页面渲染前调用 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } private void addResBody(HttpServletResponse response, ControllerRes res) throws IOException { response.setStatus(HttpServletResponse.SC_FORBIDDEN); // 设置状态码 response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); PrintWriter out = response.getWriter(); out.write(JSONObject.toJSONString(res)); out.flush(); out.close(); }}

config/PermissionWebConfig.java拦截器拦截规则

 

  

package com.cxstar.config;import com.cxstar.interceptor.PermisssionInterceptor;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class PermissionWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new PermisssionInterceptor()) .addPathPatterns("/**") // 拦截哪些页面 .excludePathPatterns("/user/login", "/user/register"); // 放行哪些页面 }}

 

  

三、测试

1.注册

注册成功

 

  

 

  数据入库

  

 

  

2.登录

登录成功

 

  

 

  查看登录后返回的token

  

 

  

 

  

3.状态续签【登录保持】

使用上一步登录返回的 token 请求改密业务

 

  

 

  当 JWT 存在时间小于 JWT 过期时间的一半时业务会执行成功执行业务不会返回续签的 token

  

 

  当 JWT 存在时间大于 JWT 过期时间的一半 且 小于过期时间 时业务会执行成功执行业务会返回续签的 token,前端的下次请求需要使用新续签的 token

  

 

  当 JWT 存在时间大于 JWT 过期时间 时业务会执行失败执行业务不会返回续签的 token

  

 

  到此这篇关于SpringBoot+JWT实现注册、登录、状态续签的实战教程的文章就介绍到这了,更多相关SpringBoot JWT登录状态续签内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: