用户登录拦截,threadLocal基本开发完成

This commit is contained in:
jieyuu 2024-07-01 16:39:07 +08:00
parent 6e6423f583
commit 86a1c4cf65
12 changed files with 373 additions and 16 deletions

View File

@ -132,7 +132,12 @@
<version>${minio.version}</version> <version>${minio.version}</version>
</dependency> </dependency>
<!-- JWT相关 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@ -95,8 +95,12 @@
<artifactId>commons-codec</artifactId> <artifactId>commons-codec</artifactId>
</dependency> </dependency>
<!-- JWT相关 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
</dependencies> </dependencies>
@ -106,8 +110,4 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
</project> </project>

View File

@ -28,6 +28,7 @@ public enum BizCodeEnum {
ACCOUNT_REPEAT(250001, "账号已经存在"), ACCOUNT_REPEAT(250001, "账号已经存在"),
ACCOUNT_UNREGISTER(250002, "账号不存在"), ACCOUNT_UNREGISTER(250002, "账号不存在"),
ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"), ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"),
ACCOUNT_UNLOGIN(250004, "账号未登录"),
FILE_UPLOAD_USER_IMG_FAIL(600101, "用户头像文件上传失败"); FILE_UPLOAD_USER_IMG_FAIL(600101, "用户头像文件上传失败");
@Getter @Getter

View File

@ -0,0 +1,68 @@
package net.jieyuu.interceptor;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import net.jieyuu.enums.BizCodeEnum;
import net.jieyuu.model.LoginUser;
import net.jieyuu.utils.CommonUtil;
import net.jieyuu.utils.JWTUtil;
import net.jieyuu.utils.JsonData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String accessToken = request.getHeader("token");
if (accessToken == null) {
accessToken = request.getParameter("token");
}
//不为空
if (StringUtils.isNotBlank(accessToken)) {
Claims claims = JWTUtil.checkJWT(accessToken);
if (claims == null) {
//未登录
CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));
return false;
}
long userId = Long.valueOf(claims.get("id").toString());
String headImg = (String) claims.get("head_img");
String name = (String) claims.get("name");
String mail = (String) claims.get("mail");
LoginUser loginUser = new LoginUser();
loginUser.setId(userId);
loginUser.setMail(mail);
loginUser.setHeadImg(headImg);
loginUser.setName(name);
//通过threadlocal传递用户信息
threadLocal.set(loginUser);
return true;
}
//未登录
CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));
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);
}
}

View File

@ -0,0 +1,29 @@
package net.jieyuu.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class LoginUser {
/**
* 主键
*/
private Long id;
/**
* 名称
*/
private String name;
/**
* 头像
*/
@JsonProperty("head_img")
private String headImg;
/**
* 邮箱
*/
private String mail;
}

View File

@ -1,12 +1,20 @@
package net.jieyuu.utils; package net.jieyuu.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.Random; import java.util.Random;
import java.util.UUID; import java.util.UUID;
@Slf4j
public class CommonUtil { public class CommonUtil {
/** /**
@ -108,11 +116,13 @@ public class CommonUtil {
/** /**
* 获取当前时间戳 * 获取当前时间戳
*
* @return * @return
*/ */
public static long getCurrentTimestamp() { public static long getCurrentTimestamp() {
return System.currentTimeMillis(); return System.currentTimeMillis();
} }
/** /**
* 生成指定长度随机串 * 生成指定长度随机串
* *
@ -120,6 +130,7 @@ public class CommonUtil {
* @return * @return
*/ */
private static final String ALL_CHAR_NUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final String ALL_CHAR_NUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static String getStringNumRandom(int length) { public static String getStringNumRandom(int length) {
//生成随机数字和字母, //生成随机数字和字母,
Random random = new Random(); Random random = new Random();
@ -130,4 +141,26 @@ public class CommonUtil {
return saltString.toString(); return saltString.toString();
} }
/**
* 响应json数据给前端
* @param response
* @param obj
*/
public static void sendJsonMessage(HttpServletResponse response, Object obj) {
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json; charset=utf-8");
try(PrintWriter writer=response.getWriter()){
writer.print(objectMapper.writeValueAsString(obj));
writer.close();
response.flushBuffer();
}catch(IOException e){
log.warn("响应json数据给前端异常:{}",e);
}
}
} }

View File

@ -0,0 +1,81 @@
package net.jieyuu.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import net.jieyuu.model.LoginUser;
import java.util.Date;
@Slf4j
public class JWTUtil {
/**
* 过期时间
* 1000毫秒 * 60秒 * 60分钟 * 24小时 * 7天 * 10
*/
private static final long EXPIRE = 1000 * 60 * 60 * 24 * 7 * 10;
/**
* 加密密钥
*/
private static final String SECRET = "jieyuu.xyz";
/**
* 令牌前缀
*/
private static final String TOKEN_PREFIX = "Jieyuushop";
/**
* 令牌前缀
*/
private static final String SUBJECT = "Jieyuu";
/**
* 根据用户信息生成令牌
*
* @param loginUser
* @return
*/
public static String geneJsonWebToken(LoginUser loginUser) {
if (loginUser == null) {
throw new NullPointerException("loginUser对象为空");
}
long userid = loginUser.getId();
String token = Jwts.builder().setSubject(SUBJECT)
.claim("head_img", loginUser.getHeadImg())
.claim("id", userid)
.claim("name", loginUser.getName())
.claim("mail", loginUser.getMail())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.signWith(SignatureAlgorithm.HS256, SECRET).compact();
token = TOKEN_PREFIX + token;
return token;
}
/**
* 校验token的方法
*
* @param token
* @return
*/
public static Claims checkJWT(String token) {
try {
final Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody();
return claims;
} catch (Exception e) {
log.info("jwt token解密失败");
return null;
}
}
}

View File

@ -0,0 +1,30 @@
package net.jieyuu.config;
import lombok.extern.slf4j.Slf4j;
import net.jieyuu.interceptor.LoginInterceptor;
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;
@Configuration
@Slf4j
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
public LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(loginInterceptor())
//拦截的路径
.addPathPatterns("/api/user/*/**", "/api/address/*/**")
//放行的路径
.excludePathPatterns("/api/user/*/send_code", "/api/user/*/captcha",
"/api/user/*/register", "/api/user/*/login", "/api/user/*/upload");
}
}

View File

@ -5,17 +5,21 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiParam;
import net.jieyuu.enums.BizCodeEnum; import net.jieyuu.enums.BizCodeEnum;
import net.jieyuu.request.UserLoginRequest;
import net.jieyuu.request.UserRegisterRequest; import net.jieyuu.request.UserRegisterRequest;
import net.jieyuu.service.AddressService; import net.jieyuu.service.AddressService;
import net.jieyuu.service.FileService; import net.jieyuu.service.FileService;
import net.jieyuu.service.NotifyService; import net.jieyuu.service.NotifyService;
import net.jieyuu.service.UserService; import net.jieyuu.service.UserService;
import net.jieyuu.utils.CommonUtil;
import net.jieyuu.utils.JsonData; import net.jieyuu.utils.JsonData;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
/** /**
* <p> * <p>
@ -73,10 +77,35 @@ public class UserController {
public JsonData register( public JsonData register(
@ApiParam(value = "用户注册对象", required = true) @ApiParam(value = "用户注册对象", required = true)
@RequestBody UserRegisterRequest registerRequest) { @RequestBody UserRegisterRequest registerRequest) {
userService.register(registerRequest); JsonData jsonData = userService.register(registerRequest);
return JsonData.buildSuccess(); return jsonData;
} }
/**
* 登录
*
* @param loginRequest
* @return
*/
@ApiOperation("用户登录")
@PostMapping("login")
public JsonData userLogin(@ApiParam("用户登陆对象") @RequestBody UserLoginRequest loginRequest) {
JsonData jsonData = userService.login(loginRequest);
return jsonData;
}
@ApiOperation("刷新accessToken")
@PostMapping("refresh")
public JsonData getRefreshToken(Map<String, Object> param) {
//先从redis寻找refresh_token
//refresh_token存在, 解密accessToken
//重新调用JWTUtil 重新生成token
return null;
//不存在 重新登录
}
} }

View File

@ -0,0 +1,17 @@
package net.jieyuu.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "登录对象", description = "用户登录请求对象")
public class UserLoginRequest {
@ApiModelProperty(value = "邮箱", example = "123456@qq.com")
private String mail;
@ApiModelProperty(value = "密码", example = "123456")
private String pwd;
}

View File

@ -2,6 +2,7 @@ package net.jieyuu.service;
import net.jieyuu.model.UserDO; import net.jieyuu.model.UserDO;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import net.jieyuu.request.UserLoginRequest;
import net.jieyuu.request.UserRegisterRequest; import net.jieyuu.request.UserRegisterRequest;
import net.jieyuu.utils.JsonData; import net.jieyuu.utils.JsonData;
@ -15,5 +16,9 @@ import net.jieyuu.utils.JsonData;
*/ */
public interface UserService extends IService<UserDO> { public interface UserService extends IService<UserDO> {
//用户注册
JsonData register(UserRegisterRequest userRegisterRequest); JsonData register(UserRegisterRequest userRegisterRequest);
//用户登录
JsonData login(UserLoginRequest loginRequest);
} }

View File

@ -1,23 +1,31 @@
package net.jieyuu.service.impl; package net.jieyuu.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.jieyuu.enums.BizCodeEnum; import net.jieyuu.enums.BizCodeEnum;
import net.jieyuu.enums.SendCodeEnum; import net.jieyuu.enums.SendCodeEnum;
import net.jieyuu.model.LoginUser;
import net.jieyuu.model.UserDO; import net.jieyuu.model.UserDO;
import net.jieyuu.mapper.UserMapper; import net.jieyuu.mapper.UserMapper;
import net.jieyuu.request.UserLoginRequest;
import net.jieyuu.request.UserRegisterRequest; import net.jieyuu.request.UserRegisterRequest;
import net.jieyuu.service.NotifyService; import net.jieyuu.service.NotifyService;
import net.jieyuu.service.UserService; import net.jieyuu.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.jieyuu.utils.CommonUtil; import net.jieyuu.utils.CommonUtil;
import net.jieyuu.utils.JWTUtil;
import net.jieyuu.utils.JsonData; import net.jieyuu.utils.JsonData;
import org.apache.commons.codec.digest.Md5Crypt; import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* <p> * <p>
@ -32,10 +40,13 @@ import java.util.Date;
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService { public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService {
@Autowired @Autowired
NotifyService notifyService; private NotifyService notifyService;
@Autowired @Autowired
UserMapper userMapper; private UserMapper userMapper;
@Autowired
private RedisTemplate redisTemplate;
/** /**
* 用户注册 * 用户注册
@ -67,9 +78,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements
//设置创建时间 //设置创建时间
userDO.setCreateTime(new Date()); userDO.setCreateTime(new Date());
//todo 设置密码 加密 //设置密码 加密
// userDO.setPwd(userRegisterRequest.getPwd()); // userDO.setPwd(userRegisterRequest.getPwd());
//生成密钥 //生成密钥 e
userDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8)); userDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8));
//密码加盐处理 //密码加盐处理
@ -99,8 +110,12 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements
* @return * @return
*/ */
private boolean checkUnique(String mail) { private boolean checkUnique(String mail) {
//todo : 实现 //todo 高并发的思考
return true; QueryWrapper queryWrapper = new QueryWrapper<UserDO>().eq("mail", mail);
List<UserDO> list = userMapper.selectList(queryWrapper);
return list.size() > 0 ? false : true;
} }
@ -112,4 +127,48 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements
private void userRegisterInitTask(UserDO userDO) { private void userRegisterInitTask(UserDO userDO) {
} }
/**
* 用户登陆注册
* <p>
* 判断用户注册情况
* 使用密钥+密文加密判断密文匹配
*
* @param loginRequest
* @return
*/
@Override
public JsonData login(UserLoginRequest loginRequest) {
List<UserDO> userDOList = userMapper.selectList(new QueryWrapper<UserDO>().eq("mail", loginRequest.getMail()));
if (userDOList != null && userDOList.size() == 1) {
//已经注册
UserDO userDO = userDOList.get(0);
String cryptPwd = Md5Crypt.md5Crypt(loginRequest.getPwd().getBytes(), userDO.getSecret());
if (cryptPwd.equals(userDO.getPwd())) {
//登陆成功
//生成token令牌并且返回
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(userDO, loginUser);
//accessToken
String accessToken = JWTUtil.geneJsonWebToken(loginUser);
// accessToken过期时间
//UUID生成一个token
String refreshToken = CommonUtil.generateUUID();
// redisTemplate.opsForValue().set(refreshToken, "1", 1000 * 60 * 60 * 24 * 30);
return JsonData.buildSuccess(accessToken);
} else {
return JsonData.buildResult(BizCodeEnum.ACCOUNT_PWD_ERROR);
}
} else {
//未注册
//这里选择 账号或密码错误防止被爆破
return JsonData.buildResult(BizCodeEnum.ACCOUNT_PWD_ERROR);
}
}
} }