35 changed files with 877 additions and 98 deletions
@ -0,0 +1,8 @@ |
|||
package cn.nla.common.constant; |
|||
|
|||
public class CacheKey { |
|||
/** |
|||
* 注册验证码,第一个是类型,第二个是接收号码 |
|||
*/ |
|||
public static final String CHECK_CODE_KEY = "code:%s:%s"; |
|||
} |
@ -0,0 +1,22 @@ |
|||
package cn.nla.common.enums; |
|||
|
|||
/** |
|||
* 收货地址状态 |
|||
*/ |
|||
public enum AddressStatusEnum { |
|||
/** |
|||
* 是默认收货地址 |
|||
*/ |
|||
DEFAULT_STATUS(1), |
|||
/** |
|||
* 非默认收货地址 |
|||
*/ |
|||
COMMON_STATUS(0); |
|||
private final int status; |
|||
AddressStatusEnum(int status) { |
|||
this.status = status; |
|||
} |
|||
public int getStatus() { |
|||
return status; |
|||
} |
|||
} |
@ -0,0 +1,8 @@ |
|||
package cn.nla.common.enums; |
|||
|
|||
public enum SendCodeEnum { |
|||
/** |
|||
* 用户注册 |
|||
*/ |
|||
USER_REGISTER; |
|||
} |
@ -0,0 +1,60 @@ |
|||
package cn.nla.common.interceptor; |
|||
|
|||
import cn.nla.common.model.LoginUser; |
|||
import cn.nla.common.util.CommonUtil; |
|||
import cn.nla.common.util.JWTUtil; |
|||
import cn.nla.common.util.JsonData; |
|||
import io.jsonwebtoken.Claims; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
|
|||
/** |
|||
* 登录拦截器 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
public class LoginInterceptor implements HandlerInterceptor { |
|||
|
|||
public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>(); |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { |
|||
try { |
|||
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.buildError("登录过期,请重新登录")); |
|||
return false; |
|||
} |
|||
Long id = Long.valueOf(claims.get("id").toString()); |
|||
String headImg = (String) claims.get("head_img"); |
|||
String mail = (String) claims.get("mail"); |
|||
String name = (String) claims.get("name"); |
|||
|
|||
LoginUser loginUser = new LoginUser(); |
|||
loginUser.setName(name); |
|||
loginUser.setHeadImg(headImg); |
|||
loginUser.setId(id); |
|||
loginUser.setMail(mail); |
|||
log.info("用户信息:{}", loginUser); |
|||
//通过threadLocal传递用户登录信息
|
|||
threadLocal.set(loginUser); |
|||
return true; |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("拦截器错误", e); |
|||
} |
|||
CommonUtil.sendJsonMessage(response, JsonData.buildError("token不存在,请重新登录")); |
|||
return false; |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
package cn.nla.common.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; |
|||
} |
@ -0,0 +1,62 @@ |
|||
package cn.nla.common.util; |
|||
|
|||
import cn.nla.common.model.LoginUser; |
|||
import io.jsonwebtoken.Claims; |
|||
import io.jsonwebtoken.Jwts; |
|||
import io.jsonwebtoken.SignatureAlgorithm; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.Date; |
|||
|
|||
@Slf4j |
|||
public class JWTUtil { |
|||
|
|||
/** |
|||
* token 过期时间,正常是7天,方便测试我们改为70 |
|||
*/ |
|||
private static final long EXPIRE = 1000L * 60 * 60 * 24 * 7 * 10; |
|||
/** |
|||
* 加密的秘钥 |
|||
*/ |
|||
private static final String SECRET = "yuan.net666"; |
|||
/** |
|||
* 令牌前缀 |
|||
*/ |
|||
private static final String TOKEN_PREFIX = "yuan1024shop"; |
|||
/** |
|||
* subject |
|||
*/ |
|||
private static final String SUBJECT = "yuan"; |
|||
/** |
|||
* 根据用户信息,生成令牌 |
|||
*/ |
|||
public static String geneJsonWebToken(LoginUser loginUser) { |
|||
if (loginUser == null) { |
|||
throw new NullPointerException("loginUser对象为空"); |
|||
} |
|||
String token = Jwts.builder().setSubject(SUBJECT) |
|||
//payload
|
|||
.claim("head_img", loginUser.getHeadImg()) |
|||
.claim("id", loginUser.getId()) |
|||
.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的方法 |
|||
*/ |
|||
public static Claims checkJWT(String token) { |
|||
try { |
|||
return Jwts.parser() |
|||
.setSigningKey(SECRET) |
|||
.parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody(); |
|||
} catch (Exception e) { |
|||
log.info("jwt token解密失败"); |
|||
return null; |
|||
} |
|||
} |
|||
} |
@ -1,21 +1,48 @@ |
|||
package cn.nla.user.controller; |
|||
|
|||
import cn.nla.common.util.JsonData; |
|||
import cn.nla.user.model.request.UserLoginRequest; |
|||
import cn.nla.user.model.request.UserRegisterRequest; |
|||
import cn.nla.user.service.UserService; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import io.swagger.annotations.ApiParam; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
|
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import javax.annotation.Resource; |
|||
|
|||
/** |
|||
* <p> |
|||
* 电商-用户表 前端控制器 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-07-29 |
|||
*/ |
|||
@Api(tags = "用户中心控制器") |
|||
@RestController |
|||
@RequestMapping("/userDO") |
|||
@RequestMapping("/user/center/v1") |
|||
public class UserController { |
|||
|
|||
@Resource |
|||
private UserService userService; |
|||
|
|||
@ApiOperation("用户注册") |
|||
@PostMapping("register") |
|||
public JsonData register(@ApiParam("用户注册对象") @RequestBody UserRegisterRequest registerRequest) { |
|||
return userService.register(registerRequest); |
|||
} |
|||
|
|||
@ApiOperation("用户登录") |
|||
@PostMapping("login") |
|||
public JsonData login(@ApiParam("用户登录对象") @RequestBody UserLoginRequest userLoginRequest){ |
|||
return userService.login(userLoginRequest); |
|||
} |
|||
|
|||
/** |
|||
* 用户个人信息查询 |
|||
*/ |
|||
@ApiOperation("个人信息查询") |
|||
@GetMapping("detail") |
|||
public JsonData detail(){ |
|||
return JsonData.buildSuccess(userService.findUserDetail()); |
|||
} |
|||
} |
|||
|
|||
|
@ -1,10 +1,11 @@ |
|||
package cn.nla.user.model; |
|||
package cn.nla.user.model.DO; |
|||
|
|||
import lombok.Data; |
|||
import javax.validation.constraints.Min; |
|||
import javax.validation.constraints.NotEmpty; |
|||
|
|||
@Data |
|||
public class TeacherBean { |
|||
public class TeacherDO { |
|||
@NotEmpty(message = "老师姓名不能为空") |
|||
private String teacherName; |
|||
@Min(value = 1, message = "学科类型从1开始计算") |
@ -0,0 +1,48 @@ |
|||
package cn.nla.user.model.VO; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 收货地址返回对象 |
|||
*/ |
|||
@Data |
|||
public class AddressVO { |
|||
|
|||
private Long id; |
|||
/** |
|||
* 用户id |
|||
*/ |
|||
private Long userId; |
|||
/** |
|||
* 是否默认收货地址:0->否;1->是 |
|||
*/ |
|||
@JsonProperty("default_status") |
|||
private Integer defaultStatus; |
|||
/** |
|||
* 收发货人姓名 |
|||
*/ |
|||
@JsonProperty("receive_name") |
|||
private String receiveName; |
|||
/** |
|||
* 收货人电话 |
|||
*/ |
|||
private String phone; |
|||
/** |
|||
* 省/直辖市 |
|||
*/ |
|||
private String province; |
|||
/** |
|||
* 市 |
|||
*/ |
|||
private String city; |
|||
/** |
|||
* 区 |
|||
*/ |
|||
private String region; |
|||
/** |
|||
* 详细地址 |
|||
*/ |
|||
@JsonProperty("detail_address") |
|||
private String detailAddress; |
|||
} |
@ -0,0 +1,38 @@ |
|||
package cn.nla.user.model.VO; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 用户响应对象 |
|||
*/ |
|||
@Data |
|||
public class UserVO { |
|||
|
|||
private Long id; |
|||
/** |
|||
* 昵称 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
@JsonProperty("head_img") |
|||
private String headImg; |
|||
/** |
|||
* 用户签名 |
|||
*/ |
|||
private String slogan; |
|||
/** |
|||
* 0表示女,1表示男 |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 积分 |
|||
*/ |
|||
private Integer points; |
|||
/** |
|||
* 邮箱 |
|||
*/ |
|||
private String mail; |
|||
} |
@ -0,0 +1,52 @@ |
|||
package cn.nla.user.model.request; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@ApiModel(value = "地址对象", description = "新增收货地址对象") |
|||
public class AddressAddRequest { |
|||
|
|||
/** |
|||
* 是否默认收货地址:0->否;1->是 |
|||
*/ |
|||
@ApiModelProperty(value = "是否是否默认收货地址,0->否;1->是", example = "0") |
|||
@JsonProperty("default_status") |
|||
private Integer defaultStatus; |
|||
/** |
|||
* 收发货人姓名 |
|||
*/ |
|||
@ApiModelProperty(value = "收发货人姓名", example = "yuan-paas") |
|||
@JsonProperty("receive_name") |
|||
private String receiveName; |
|||
/** |
|||
* 收货人电话 |
|||
*/ |
|||
@ApiModelProperty(value = "收货人电话", example = "11111111111") |
|||
private String phone; |
|||
/** |
|||
* 省/直辖市 |
|||
*/ |
|||
@ApiModelProperty(value = "省/直辖市", example = "四川省") |
|||
private String province; |
|||
/** |
|||
* 市 |
|||
*/ |
|||
@ApiModelProperty(value = "城市", example = "成都市") |
|||
private String city; |
|||
|
|||
/** |
|||
* 区 |
|||
*/ |
|||
@ApiModelProperty(value = "区", example = "武侯区") |
|||
private String region; |
|||
|
|||
/** |
|||
* 详细地址 |
|||
*/ |
|||
@ApiModelProperty(value = "详细地址", example = "天府新区18号") |
|||
@JsonProperty("detail_address") |
|||
private String detailAddress; |
|||
} |
@ -0,0 +1,16 @@ |
|||
package cn.nla.user.model.request; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@ApiModel(value = "登录对象", description = "用户登录请求对象") |
|||
public class UserLoginRequest { |
|||
|
|||
@ApiModelProperty(value = "邮箱", example = "794666918@qq.com") |
|||
private String mail; |
|||
@ApiModelProperty(value = "密码", example = "123456") |
|||
private String pwd; |
|||
|
|||
} |
@ -0,0 +1,27 @@ |
|||
package cn.nla.user.model.request; |
|||
|
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@ApiModel(value = "用户注册对象", description = "用户注册请求对象") |
|||
@Data |
|||
public class UserRegisterRequest { |
|||
@ApiModelProperty(value = "昵称", example = "Anna小姐姐") |
|||
private String name; |
|||
@ApiModelProperty(value = "密码", example = "12345") |
|||
private String pwd; |
|||
@ApiModelProperty(value = "头像", example = "xx") |
|||
@JsonProperty("head_img") |
|||
private String headImg; |
|||
@ApiModelProperty(value = "用户个人性签名", example = "人生需要动态规划,学习需要贪心算法") |
|||
private String slogan; |
|||
@ApiModelProperty(value = "0表示女,1表示男", example = "1") |
|||
private Integer sex; |
|||
@ApiModelProperty(value = "邮箱", example = "794666918@qq.com") |
|||
private String mail; |
|||
@ApiModelProperty(value = "验证码", example = "232343") |
|||
private String code; |
|||
} |
@ -0,0 +1,21 @@ |
|||
package cn.nla.user.service; |
|||
|
|||
import cn.nla.common.enums.SendCodeEnum; |
|||
import cn.nla.common.util.JsonData; |
|||
|
|||
public interface NotifyService { |
|||
|
|||
/** |
|||
* 邮件发送验证码 |
|||
*/ |
|||
JsonData sendCode(SendCodeEnum sendCodeEnum, String to); |
|||
|
|||
/** |
|||
* 判断验证码是否一样 |
|||
* @param sendCodeEnum 验证码类型 |
|||
* @param to 接收者 |
|||
* @param code 验证码 |
|||
*/ |
|||
boolean checkCode(SendCodeEnum sendCodeEnum, String to, String code); |
|||
|
|||
} |
@ -0,0 +1,75 @@ |
|||
package cn.nla.user.service.impl; |
|||
|
|||
import cn.nla.common.constant.CacheKey; |
|||
import cn.nla.common.enums.BizCodeEnum; |
|||
import cn.nla.common.enums.SendCodeEnum; |
|||
import cn.nla.common.util.CheckUtil; |
|||
import cn.nla.common.util.CommonUtil; |
|||
import cn.nla.common.util.JsonData; |
|||
import cn.nla.user.service.MailService; |
|||
import cn.nla.user.service.NotifyService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.data.redis.core.StringRedisTemplate; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import javax.annotation.Resource; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class NotifyServiceImpl implements NotifyService { |
|||
|
|||
/** |
|||
* 图形验证码有效期10分钟 |
|||
*/ |
|||
private static final long CAPTCHA_CODE_EXPIRED = 60 * 1000 * 10; |
|||
@Resource |
|||
private MailService mailService; |
|||
|
|||
@Autowired |
|||
private StringRedisTemplate redisTemplate; |
|||
|
|||
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) { |
|||
String cacheKey = String.format(CacheKey.CHECK_CODE_KEY, sendCodeEnum.name(), to); |
|||
String cacheValue = redisTemplate.opsForValue().get(cacheKey); |
|||
//如果不为空,则判断是否60秒内重复发送
|
|||
if (StringUtils.isNotBlank(cacheValue)) { |
|||
long ttl = Long.parseLong(cacheValue.split("_")[1]); |
|||
//当前时间戳-验证码发送时间戳,如果小于60秒,则不给重复发送
|
|||
if (System.currentTimeMillis() - ttl < 1000 * 60) { |
|||
log.info("重复发送验证码,时间间隔:{} 秒", (System.currentTimeMillis() - ttl) / 1000); |
|||
return JsonData.buildResult(BizCodeEnum.CODE_LIMITED); |
|||
} |
|||
} |
|||
//拼接验证码: 验证码+时间戳: 2322_324243232424324
|
|||
String code = CommonUtil.getRandomCode(6); |
|||
String value = code + "_" + System.currentTimeMillis(); |
|||
redisTemplate.opsForValue().set(cacheKey, value, CAPTCHA_CODE_EXPIRED, TimeUnit.MILLISECONDS); |
|||
if (CheckUtil.isEmail(to)) { |
|||
//邮箱验证码
|
|||
mailService.sendMail(to, "电商验证码", code); |
|||
log.info("发送邮箱验证码[{}]成功!", code); |
|||
return JsonData.buildSuccess(); |
|||
} else if (CheckUtil.isPhone(to)) { |
|||
//短信验证码
|
|||
log.info("要发送短信验证码[{}]", code); |
|||
} |
|||
return JsonData.buildResult(BizCodeEnum.CODE_TO_ERROR); |
|||
} |
|||
|
|||
public boolean checkCode(SendCodeEnum sendCodeEnum, String to, String code) { |
|||
String cacheKey = String.format(CacheKey.CHECK_CODE_KEY, sendCodeEnum.name(), to); |
|||
String cacheValue = redisTemplate.opsForValue().get(cacheKey); |
|||
if (StringUtils.isNotBlank(cacheValue)) { |
|||
String cacheCode = cacheValue.split("_")[0]; |
|||
if (cacheCode.equals(code)) { |
|||
//删除验证码
|
|||
redisTemplate.delete(cacheKey); |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
} |
Loading…
Reference in new issue