parent
f93ca911cf
commit
e3ffa18eff
|
@ -1,3 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package cn.bunny.exception;
|
||||
|
||||
import cn.bunny.spzx.model.vo.common.ResultCodeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BunnyException extends RuntimeException {
|
||||
private Integer code;
|
||||
private String message;
|
||||
private ResultCodeEnum resultCodeEnum;
|
||||
|
||||
public BunnyException(ResultCodeEnum resultCodeEnum) {
|
||||
this.resultCodeEnum = resultCodeEnum;
|
||||
this.code = resultCodeEnum.getCode();
|
||||
this.message = resultCodeEnum.getMessage();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package cn.bunny.exception;
|
||||
|
||||
import cn.bunny.spzx.model.vo.common.Result;
|
||||
import cn.bunny.spzx.model.vo.common.ResultCodeEnum;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// 全局异常处理
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseBody
|
||||
public Result error() {
|
||||
return Result.build(null, ResultCodeEnum.SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
// 自定义异常处理
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public Result error(BunnyException bunnyException) {
|
||||
return Result.build(null, bunnyException.getResultCodeEnum());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cn.bunny;
|
||||
|
||||
import cn.bunny.spzx.model.entity.system.SysUser;
|
||||
|
||||
public class AuthContextUtil {
|
||||
// 创建ThreadLocal
|
||||
private static final ThreadLocal<SysUser> threadLocal = new ThreadLocal<>();
|
||||
|
||||
// 添加数据库
|
||||
public static void set(SysUser sysUser) {
|
||||
threadLocal.set(sysUser);
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
public static SysUser get() {
|
||||
return threadLocal.get();
|
||||
}
|
||||
|
||||
// 删除数据
|
||||
public static void remove() {
|
||||
threadLocal.remove();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cn.bunny.config;
|
||||
|
||||
import cn.bunny.interceptor.LoginAuthInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Component
|
||||
public class WebMvcConfiguration implements WebMvcConfigurer {
|
||||
@Autowired
|
||||
private LoginAuthInterceptor loginAuthInterceptor;
|
||||
|
||||
// 拦截器注册
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(loginAuthInterceptor)
|
||||
.excludePathPatterns("/admin/system/index/login", "/admin.system/index/generateValidateCode")
|
||||
.addPathPatterns("/**");
|
||||
}
|
||||
|
||||
|
||||
// 解决跨域
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowCredentials(true)
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("*")
|
||||
.allowedHeaders("*");
|
||||
}
|
||||
}
|
|
@ -1,17 +1,17 @@
|
|||
package cn.bunny.controller;
|
||||
|
||||
import cn.bunny.service.SysUserService;
|
||||
import cn.bunny.service.ValidateCodeService;
|
||||
import cn.bunny.spzx.model.dto.system.LoginDto;
|
||||
import cn.bunny.spzx.model.entity.system.SysUser;
|
||||
import cn.bunny.spzx.model.vo.common.Result;
|
||||
import cn.bunny.spzx.model.vo.common.ResultCodeEnum;
|
||||
import cn.bunny.spzx.model.vo.system.LoginVo;
|
||||
import cn.bunny.spzx.model.vo.system.ValidateCodeVo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Tag(name = "用户接口")
|
||||
@RestController
|
||||
|
@ -20,11 +20,40 @@ public class IndexController {
|
|||
@Autowired
|
||||
private SysUserService sysUserService;
|
||||
|
||||
@Autowired
|
||||
private ValidateCodeService validateCodeService;
|
||||
|
||||
// 用户登录
|
||||
@Operation(summary = "用户登录")
|
||||
@Operation(summary = "用户登录", description = "用户登录获取token")
|
||||
@PostMapping("login")
|
||||
public Result login(@RequestBody LoginDto loginDto) {
|
||||
LoginVo loginVo = sysUserService.login(loginDto);
|
||||
return Result.build(loginVo, ResultCodeEnum.SUCCESS);
|
||||
return Result.build(loginVo, ResultCodeEnum.LOGIN_SUCCESS);
|
||||
}
|
||||
|
||||
// 生成验证码
|
||||
@Operation(summary = "获取验证码", description = "会获得两个值:codeKey + codeValue")
|
||||
@GetMapping(value = "/generateValidateCode")
|
||||
public Result<ValidateCodeVo> generateValidateCode() {
|
||||
ValidateCodeVo validateCodeVo = validateCodeService.generateValidateCode();
|
||||
return Result.build(validateCodeVo, ResultCodeEnum.SUCCESS);
|
||||
}
|
||||
|
||||
// 获取用户登录信息
|
||||
@Operation(summary = "获取用户登录信息", description = "获取token")
|
||||
@GetMapping(value = "getUserInfo")
|
||||
// public Result getUserInfo(HttpServletRequest httpServletRequest) {
|
||||
// String token = httpServletRequest.getHeader("token");
|
||||
public Result getUserInfo(@RequestHeader(name = "token") String token) {
|
||||
SysUser sysUser = sysUserService.getUserInfo(token);
|
||||
return Result.build(sysUser, ResultCodeEnum.SUCCESS);
|
||||
}
|
||||
|
||||
// 用户退出
|
||||
@Operation(summary = "用户退出", description = "清除Redis中token")
|
||||
@GetMapping(".logout")
|
||||
public Result logout(@RequestHeader(name = "token") String token) {
|
||||
sysUserService.logout(token);
|
||||
return Result.build(null, ResultCodeEnum.SUCCESS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package cn.bunny.interceptor;
|
||||
|
||||
import cn.bunny.AuthContextUtil;
|
||||
import cn.bunny.spzx.model.entity.system.SysUser;
|
||||
import cn.bunny.spzx.model.vo.common.Result;
|
||||
import cn.bunny.spzx.model.vo.common.ResultCodeEnum;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class LoginAuthInterceptor implements HandlerInterceptor {
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
/**
|
||||
* 响应208给前端
|
||||
*
|
||||
* @param response
|
||||
*/
|
||||
public void respondNoLoginInfo(HttpServletResponse response) {
|
||||
Result<Object> result = Result.build(null, ResultCodeEnum.LOGIN_AUTH);
|
||||
PrintWriter writer = null;
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("text/html; charset=utf-8");
|
||||
try {
|
||||
writer = response.getWriter();
|
||||
writer.println(JSON.toJSONString(result));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (writer != null) writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
/**
|
||||
* 1. 获取骑牛方式
|
||||
* - 如果请求方式是option 预检请求,直接做放行
|
||||
* 2. 从请求中获取token
|
||||
* 3. 如果token为空,返回错误提示
|
||||
* 4. 如果token不为空,拿着token查询redis
|
||||
* 5. 如果redis查询不到数据,返回错误提示
|
||||
* 6. 如果redis查询用户信息,把用户信息放到ThreadLocal
|
||||
* 7. 更新redis更新时间
|
||||
* 放行
|
||||
*/
|
||||
|
||||
// 1. 获取骑牛方式
|
||||
String method = request.getMethod();
|
||||
if ("OPTIONS".equals(method)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 从请求中获取token
|
||||
String token = request.getHeader("token");
|
||||
|
||||
// 3. 如果token为空,返回错误提示
|
||||
if (StrUtil.isEmpty(token)) {
|
||||
respondNoLoginInfo((response));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 如果token不为空,拿着token查询redis
|
||||
String userinfoString = redisTemplate.opsForValue().get("user:login" + token);
|
||||
|
||||
// 如果redis查询不到数据返回错误提示
|
||||
if (StrUtil.isEmpty(userinfoString)) {
|
||||
respondNoLoginInfo(response);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果redis查询到用户信息,将信息放到ThreadLocal中
|
||||
SysUser sysUser = JSON.parseObject(userinfoString, SysUser.class);
|
||||
AuthContextUtil.set(sysUser);
|
||||
|
||||
// 吧redis用户信息数据更新过期时间
|
||||
redisTemplate.expire("user:login" + token, 30, TimeUnit.MINUTES);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
|
||||
AuthContextUtil.remove();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package cn.bunny;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "spzx.auth")
|
||||
public class properties {
|
||||
private List<String> noAuthUrls;
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
package cn.bunny.service;
|
||||
|
||||
import cn.bunny.spzx.model.dto.system.LoginDto;
|
||||
import cn.bunny.spzx.model.entity.system.SysUser;
|
||||
import cn.bunny.spzx.model.vo.system.LoginVo;
|
||||
|
||||
public interface SysUserService {
|
||||
// 用户登录
|
||||
LoginVo login(LoginDto loginDto);
|
||||
|
||||
// 获取用户token
|
||||
SysUser getUserInfo(String token);
|
||||
|
||||
// 用户退出
|
||||
void logout(String token);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package cn.bunny.service;
|
||||
|
||||
import cn.bunny.spzx.model.vo.system.ValidateCodeVo;
|
||||
|
||||
public interface ValidateCodeService {
|
||||
ValidateCodeVo generateValidateCode();
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
package cn.bunny.service.impl;
|
||||
|
||||
|
||||
import cn.bunny.exception.BunnyException;
|
||||
import cn.bunny.mapper.SysUserMapper;
|
||||
import cn.bunny.service.SysUserService;
|
||||
import cn.bunny.spzx.model.dto.system.LoginDto;
|
||||
import cn.bunny.spzx.model.entity.system.SysUser;
|
||||
import cn.bunny.spzx.model.vo.common.ResultCodeEnum;
|
||||
import cn.bunny.spzx.model.vo.system.LoginVo;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
@ -23,9 +26,26 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
|
||||
// 用户登录
|
||||
@Override
|
||||
public LoginVo login(LoginDto loginDto) {
|
||||
// 判断验证码是否正确
|
||||
String captcha = loginDto.getCaptcha();
|
||||
String codeKey = loginDto.getCodeKey();
|
||||
String redisCode = redisTemplate.opsForValue().get("user:validate" + codeKey);
|
||||
|
||||
System.out.println("redisCode" + redisCode);
|
||||
System.out.println("captcha" + captcha);
|
||||
|
||||
// 比较验证码
|
||||
if (StrUtil.isEmpty(redisCode) || !StrUtil.equalsIgnoreCase(redisCode, captcha)) {
|
||||
redisTemplate.delete("user:validate" + codeKey);
|
||||
throw new BunnyException(ResultCodeEnum.VALIDATECODE_ERROR);
|
||||
}
|
||||
|
||||
// 如果一致,删除Redis中验证码
|
||||
redisTemplate.delete("user:validate" + codeKey);
|
||||
|
||||
// 获取用户提交用户名,LoginDto获取到
|
||||
String userName = loginDto.getUserName();
|
||||
|
||||
|
@ -34,24 +54,36 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
|
||||
// 如果用户名查询不到对应信息,用户不存在,返回错误信息
|
||||
if (sysUser == null) {
|
||||
throw new RuntimeException("用户名不存在");
|
||||
throw new BunnyException(ResultCodeEnum.LOGIN_ERROR_USERNAME);// 用户名不存在
|
||||
}
|
||||
|
||||
// 获取输入密码,比较输入密码和数据库密码是否一致
|
||||
String databases_password = sysUser.getPassword();
|
||||
String input_password = DigestUtils.md5DigestAsHex(loginDto.getPassword().getBytes());
|
||||
|
||||
if (!input_password.equals(databases_password)) {
|
||||
throw new RuntimeException("密码不正确");
|
||||
throw new BunnyException(ResultCodeEnum.LOGIN_ERROR_PASSWORD);// 密码不正确
|
||||
}
|
||||
|
||||
|
||||
String token = UUID.randomUUID().toString().replaceAll("-", "");
|
||||
|
||||
redisTemplate.opsForValue().set("user:login" + token, JSON.toJSONString(sysUser), 7, TimeUnit.DAYS);
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setToken(token);
|
||||
|
||||
return loginVo;
|
||||
}
|
||||
|
||||
// 获取用户token
|
||||
@Override
|
||||
public SysUser getUserInfo(String token) {
|
||||
String userJson = redisTemplate.opsForValue().get("user:login" + token);
|
||||
SysUser sysUser = JSON.parseObject(userJson, SysUser.class);
|
||||
return sysUser;
|
||||
}
|
||||
|
||||
// 用户退出
|
||||
@Override
|
||||
public void logout(String token) {
|
||||
redisTemplate.delete("user:login" + token);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package cn.bunny.service.impl;
|
||||
|
||||
import cn.bunny.service.ValidateCodeService;
|
||||
import cn.bunny.spzx.model.vo.system.ValidateCodeVo;
|
||||
import cn.hutool.captcha.CaptchaUtil;
|
||||
import cn.hutool.captcha.CircleCaptcha;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class ValidateCodeServiceIImpl implements ValidateCodeService {
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
// 生成验证码
|
||||
@Override
|
||||
public ValidateCodeVo generateValidateCode() {
|
||||
// 使用hutool生成验证码
|
||||
CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(150, 40, 4, 20);
|
||||
String code = circleCaptcha.getCode();
|
||||
String imageBase64 = circleCaptcha.getImageBase64();
|
||||
|
||||
// 放到Redis
|
||||
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
|
||||
redisTemplate.opsForValue().set("user:validate" + uuid, code, 5, TimeUnit.MINUTES);
|
||||
|
||||
// 返回 validateCodeVo
|
||||
ValidateCodeVo validateCodeVo = new ValidateCodeVo();
|
||||
validateCodeVo.setCodeKey(uuid);
|
||||
validateCodeVo.setCodeValue("data:image/png;base64," + imageBase64);
|
||||
return validateCodeVo;
|
||||
}
|
||||
}
|
|
@ -3,3 +3,13 @@ spring:
|
|||
name: server-manager
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
server:
|
||||
servlet:
|
||||
context-path: /api
|
||||
|
||||
spzx:
|
||||
auth:
|
||||
noAuthUrls:
|
||||
- /admin/system/index/login
|
||||
- /admin/system/index/generateValidateCode
|
|
@ -1,30 +1,33 @@
|
|||
package cn.bunny.spzx.model.vo.common;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter // 提供获取属性值的getter方法
|
||||
public enum ResultCodeEnum {
|
||||
|
||||
SUCCESS(200 , "操作成功") ,
|
||||
LOGIN_ERROR(201 , "用户名或者密码错误"),
|
||||
VALIDATECODE_ERROR(202 , "验证码错误") ,
|
||||
LOGIN_AUTH(208 , "用户未登录"),
|
||||
USER_NAME_IS_EXISTS(209 , "用户名已经存在"),
|
||||
SYSTEM_ERROR(9999 , "您的网络有问题请稍后重试"),
|
||||
NODE_ERROR( 217, "该节点下有子节点,不可以删除"),
|
||||
SUCCESS(200, "操作成功"),
|
||||
LOGIN_SUCCESS(200, "登录成功"),
|
||||
LOGIN_ERROR(201, "用户名或者密码错误"),
|
||||
LOGIN_ERROR_USERNAME(201, "用户名错误"),
|
||||
LOGIN_ERROR_PASSWORD(201, "密码错误"),
|
||||
VALIDATECODE_ERROR(202, "验证码错误"),
|
||||
LOGIN_AUTH(208, "用户未登录"),
|
||||
USER_NAME_IS_EXISTS(209, "用户名已经存在"),
|
||||
SYSTEM_ERROR(9999, "您的网络有问题请稍后重试"),
|
||||
NODE_ERROR(217, "该节点下有子节点,不可以删除"),
|
||||
DATA_ERROR(204, "数据异常"),
|
||||
ACCOUNT_STOP( 216, "账号已停用"),
|
||||
ACCOUNT_STOP(216, "账号已停用"),
|
||||
STOCK_LESS(219, "库存不足");
|
||||
|
||||
STOCK_LESS( 219, "库存不足"),
|
||||
@Schema(description = "业务状态码")
|
||||
private final Integer code; // 业务状态码
|
||||
@Schema(description = "响应消息")
|
||||
private final String message; // 响应消息
|
||||
|
||||
;
|
||||
|
||||
private Integer code ; // 业务状态码
|
||||
private String message ; // 响应消息
|
||||
|
||||
private ResultCodeEnum(Integer code , String message) {
|
||||
this.code = code ;
|
||||
this.message = message ;
|
||||
ResultCodeEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,14 +2,17 @@ package cn.bunny.spzx.model.vo.h5;
|
|||
|
||||
import cn.bunny.spzx.model.entity.product.Category;
|
||||
import cn.bunny.spzx.model.entity.product.ProductSku;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "商品类")
|
||||
public class IndexVo {
|
||||
|
||||
private List<Category> categoryList ; // 一级分类的类别数据
|
||||
private List<ProductSku> productSkuList ; // 畅销商品列表数据
|
||||
@Schema(description = "一级分类数据")
|
||||
private List<Category> categoryList; // 一级分类的类别数据
|
||||
@Schema(description = "畅销商品列表数据")
|
||||
private List<ProductSku> productSkuList; // 畅销商品列表数据
|
||||
|
||||
}
|
|
@ -8,9 +8,9 @@ import lombok.Data;
|
|||
public class ValidateCodeVo {
|
||||
|
||||
@Schema(description = "验证码key")
|
||||
private String codeKey ; // 验证码的key
|
||||
private String codeKey;
|
||||
|
||||
@Schema(description = "验证码value")
|
||||
private String codeValue ; // 图片验证码对应的字符串数据
|
||||
private String codeValue;
|
||||
|
||||
}
|
Loading…
Reference in New Issue