From 86a1c4cf654701290e88f3593db08c0664a25fe3 Mon Sep 17 00:00:00 2001 From: jieyuu <645634619@qq.com> Date: Mon, 1 Jul 2024 16:39:07 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=99=BB=E5=BD=95=E6=8B=A6?= =?UTF-8?q?=E6=88=AA=EF=BC=8CthreadLocal=E5=9F=BA=E6=9C=AC=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 +- xdclass-common/pom.xml | 12 +-- .../java/net/jieyuu/enums/BizCodeEnum.java | 1 + .../jieyuu/interceptor/LoginInterceptor.java | 68 ++++++++++++++++ .../main/java/net/jieyuu/model/LoginUser.java | 29 +++++++ .../java/net/jieyuu/utils/CommonUtil.java | 35 +++++++- .../main/java/net/jieyuu/utils/JWTUtil.java | 81 +++++++++++++++++++ .../net/jieyuu/config/InterceptorConfig.java | 30 +++++++ .../net/jieyuu/controller/UserController.java | 33 +++++++- .../net/jieyuu/request/UserLoginRequest.java | 17 ++++ .../java/net/jieyuu/service/UserService.java | 5 ++ .../jieyuu/service/impl/UserServiceImpl.java | 71 ++++++++++++++-- 12 files changed, 373 insertions(+), 16 deletions(-) create mode 100644 xdclass-common/src/main/java/net/jieyuu/interceptor/LoginInterceptor.java create mode 100644 xdclass-common/src/main/java/net/jieyuu/model/LoginUser.java create mode 100644 xdclass-common/src/main/java/net/jieyuu/utils/JWTUtil.java create mode 100644 xdclass-user-service/src/main/java/net/jieyuu/config/InterceptorConfig.java create mode 100644 xdclass-user-service/src/main/java/net/jieyuu/request/UserLoginRequest.java diff --git a/pom.xml b/pom.xml index 2918b77..5f799da 100644 --- a/pom.xml +++ b/pom.xml @@ -132,7 +132,12 @@ ${minio.version} - + + + io.jsonwebtoken + jjwt + 0.7.0 + diff --git a/xdclass-common/pom.xml b/xdclass-common/pom.xml index cbc80a6..ebd4fad 100644 --- a/xdclass-common/pom.xml +++ b/xdclass-common/pom.xml @@ -95,8 +95,12 @@ commons-codec - - + + + io.jsonwebtoken + jjwt + 0.7.0 + @@ -106,8 +110,4 @@ UTF-8 - - - - \ No newline at end of file diff --git a/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java b/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java index aa50312..fa39278 100644 --- a/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java +++ b/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java @@ -28,6 +28,7 @@ public enum BizCodeEnum { ACCOUNT_REPEAT(250001, "账号已经存在"), ACCOUNT_UNREGISTER(250002, "账号不存在"), ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"), + ACCOUNT_UNLOGIN(250004, "账号未登录"), FILE_UPLOAD_USER_IMG_FAIL(600101, "用户头像文件上传失败"); @Getter diff --git a/xdclass-common/src/main/java/net/jieyuu/interceptor/LoginInterceptor.java b/xdclass-common/src/main/java/net/jieyuu/interceptor/LoginInterceptor.java new file mode 100644 index 0000000..76c5a79 --- /dev/null +++ b/xdclass-common/src/main/java/net/jieyuu/interceptor/LoginInterceptor.java @@ -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 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); + } +} diff --git a/xdclass-common/src/main/java/net/jieyuu/model/LoginUser.java b/xdclass-common/src/main/java/net/jieyuu/model/LoginUser.java new file mode 100644 index 0000000..5d337f2 --- /dev/null +++ b/xdclass-common/src/main/java/net/jieyuu/model/LoginUser.java @@ -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; + +} diff --git a/xdclass-common/src/main/java/net/jieyuu/utils/CommonUtil.java b/xdclass-common/src/main/java/net/jieyuu/utils/CommonUtil.java index 03ea281..d05dbbb 100644 --- a/xdclass-common/src/main/java/net/jieyuu/utils/CommonUtil.java +++ b/xdclass-common/src/main/java/net/jieyuu/utils/CommonUtil.java @@ -1,12 +1,20 @@ package net.jieyuu.utils; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; + 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.UnknownHostException; import java.security.MessageDigest; import java.util.Random; import java.util.UUID; +@Slf4j public class CommonUtil { /** @@ -108,11 +116,13 @@ public class CommonUtil { /** * 获取当前时间戳 + * * @return */ - public static long getCurrentTimestamp(){ + public static long getCurrentTimestamp() { return System.currentTimeMillis(); } + /** * 生成指定长度随机串 * @@ -120,6 +130,7 @@ public class CommonUtil { * @return */ private static final String ALL_CHAR_NUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + public static String getStringNumRandom(int length) { //生成随机数字和字母, Random random = new Random(); @@ -130,4 +141,26 @@ public class CommonUtil { 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); + } + + } + } diff --git a/xdclass-common/src/main/java/net/jieyuu/utils/JWTUtil.java b/xdclass-common/src/main/java/net/jieyuu/utils/JWTUtil.java new file mode 100644 index 0000000..149eb13 --- /dev/null +++ b/xdclass-common/src/main/java/net/jieyuu/utils/JWTUtil.java @@ -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; + } + } +} diff --git a/xdclass-user-service/src/main/java/net/jieyuu/config/InterceptorConfig.java b/xdclass-user-service/src/main/java/net/jieyuu/config/InterceptorConfig.java new file mode 100644 index 0000000..8226b8f --- /dev/null +++ b/xdclass-user-service/src/main/java/net/jieyuu/config/InterceptorConfig.java @@ -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"); + } +} diff --git a/xdclass-user-service/src/main/java/net/jieyuu/controller/UserController.java b/xdclass-user-service/src/main/java/net/jieyuu/controller/UserController.java index e2b5833..9f877a7 100644 --- a/xdclass-user-service/src/main/java/net/jieyuu/controller/UserController.java +++ b/xdclass-user-service/src/main/java/net/jieyuu/controller/UserController.java @@ -5,17 +5,21 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import net.jieyuu.enums.BizCodeEnum; +import net.jieyuu.request.UserLoginRequest; import net.jieyuu.request.UserRegisterRequest; import net.jieyuu.service.AddressService; import net.jieyuu.service.FileService; import net.jieyuu.service.NotifyService; import net.jieyuu.service.UserService; +import net.jieyuu.utils.CommonUtil; import net.jieyuu.utils.JsonData; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpRequest; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.Map; /** *

@@ -73,10 +77,35 @@ public class UserController { public JsonData register( @ApiParam(value = "用户注册对象", required = true) @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 param) { + //先从redis寻找refresh_token + //refresh_token存在, 解密accessToken + //重新调用JWTUtil 重新生成token + + return null; + //不存在 重新登录 + } } diff --git a/xdclass-user-service/src/main/java/net/jieyuu/request/UserLoginRequest.java b/xdclass-user-service/src/main/java/net/jieyuu/request/UserLoginRequest.java new file mode 100644 index 0000000..ad5e2e1 --- /dev/null +++ b/xdclass-user-service/src/main/java/net/jieyuu/request/UserLoginRequest.java @@ -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; +} diff --git a/xdclass-user-service/src/main/java/net/jieyuu/service/UserService.java b/xdclass-user-service/src/main/java/net/jieyuu/service/UserService.java index a8c3d92..c2c99f8 100644 --- a/xdclass-user-service/src/main/java/net/jieyuu/service/UserService.java +++ b/xdclass-user-service/src/main/java/net/jieyuu/service/UserService.java @@ -2,6 +2,7 @@ package net.jieyuu.service; import net.jieyuu.model.UserDO; import com.baomidou.mybatisplus.extension.service.IService; +import net.jieyuu.request.UserLoginRequest; import net.jieyuu.request.UserRegisterRequest; import net.jieyuu.utils.JsonData; @@ -15,5 +16,9 @@ import net.jieyuu.utils.JsonData; */ public interface UserService extends IService { + //用户注册 JsonData register(UserRegisterRequest userRegisterRequest); + + //用户登录 + JsonData login(UserLoginRequest loginRequest); } diff --git a/xdclass-user-service/src/main/java/net/jieyuu/service/impl/UserServiceImpl.java b/xdclass-user-service/src/main/java/net/jieyuu/service/impl/UserServiceImpl.java index dea47ca..28ac379 100644 --- a/xdclass-user-service/src/main/java/net/jieyuu/service/impl/UserServiceImpl.java +++ b/xdclass-user-service/src/main/java/net/jieyuu/service/impl/UserServiceImpl.java @@ -1,23 +1,31 @@ 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 net.jieyuu.enums.BizCodeEnum; import net.jieyuu.enums.SendCodeEnum; +import net.jieyuu.model.LoginUser; import net.jieyuu.model.UserDO; import net.jieyuu.mapper.UserMapper; +import net.jieyuu.request.UserLoginRequest; import net.jieyuu.request.UserRegisterRequest; import net.jieyuu.service.NotifyService; import net.jieyuu.service.UserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import net.jieyuu.utils.CommonUtil; +import net.jieyuu.utils.JWTUtil; import net.jieyuu.utils.JsonData; import org.apache.commons.codec.digest.Md5Crypt; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.Date; +import java.util.List; /** *

@@ -32,10 +40,13 @@ import java.util.Date; public class UserServiceImpl extends ServiceImpl implements UserService { @Autowired - NotifyService notifyService; + private NotifyService notifyService; @Autowired - UserMapper userMapper; + private UserMapper userMapper; + + @Autowired + private RedisTemplate redisTemplate; /** * 用户注册 @@ -67,9 +78,9 @@ public class UserServiceImpl extends ServiceImpl implements //设置创建时间 userDO.setCreateTime(new Date()); - //todo 设置密码 加密 + //设置密码 加密 // userDO.setPwd(userRegisterRequest.getPwd()); - //生成密钥 盐 + //生成密钥 盐e userDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8)); //密码加盐处理 @@ -99,8 +110,12 @@ public class UserServiceImpl extends ServiceImpl implements * @return */ private boolean checkUnique(String mail) { - //todo : 实现 - return true; + //todo 高并发的思考 + QueryWrapper queryWrapper = new QueryWrapper().eq("mail", mail); + + List list = userMapper.selectList(queryWrapper); + + return list.size() > 0 ? false : true; } @@ -112,4 +127,48 @@ public class UserServiceImpl extends ServiceImpl implements private void userRegisterInitTask(UserDO userDO) { } + + /** + * 用户登陆注册 + *

+ * 判断用户注册情况 + * 使用密钥+密文加密,判断密文匹配 + * + * @param loginRequest + * @return + */ + @Override + public JsonData login(UserLoginRequest loginRequest) { + List userDOList = userMapper.selectList(new QueryWrapper().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); + } + } + + }