访客登录认证模块

This commit is contained in:
zjf committed 2025-07-21 17:56:54 +08:00
1 parent 4f688ffd2e
commit a895c16ee2
7 files changed
+200 -29

No files matched your search

@@ -20,7 +20,7 @@ public interface CacheConstants {
*/
String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY:";
String VISITOR_CODE_KEY = "visitor_code_key";
String VISITOR_CODE_KEY = "visitor_code_key:";
String SMS_CODE_KEY = "smsCode_";
@@ -14,11 +14,16 @@
<dependencies>
<!-- Token-->
<!-- <dependency>-->
<!-- <groupId>io.jsonwebtoken</groupId>-->
<!-- <artifactId>jjwt</artifactId>-->
<!-- <version>0.9.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
<groupId>cn.hutool</groupId>
<artifactId>hutool-jwt</artifactId>
</dependency>
<!-- 幂等插件使用 -->
<dependency>
<groupId>com.bsd</groupId>
@@ -0,0 +1,71 @@
package com.bsd.pjcloud.integrated.config;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.bsd.pjcloud.integrated.service.VisitorsService;
import com.bsd.pjcloud.integrated.service.VisitorUserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class VisitorTokenInterceptor implements HandlerInterceptor {
@Value("${visitor.jwt-secret}")
private String secret;
private final VisitorUserService visitorUserService;
public VisitorTokenInterceptor(@Lazy VisitorUserService visitorUserService) {
this.visitorUserService = visitorUserService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if (token == null || token.isEmpty()) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: token missing");
return false;
}
JWT jwt = JWTUtil.parseToken(token);
boolean verify = jwt.setKey(secret.getBytes()).verify();
if (!verify) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: token invalid");
return false;
}
// 校验过期时间
Object expObj = jwt.getPayload("exp");
if (expObj == null || Long.parseLong(expObj.toString()) < System.currentTimeMillis()) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: token expired");
return false;
}
Object userType = jwt.getPayload("userType");
if (userType == null || !"visitor".equals(userType.toString())) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: not visitor");
return false;
}
// 账号状态校验
Object idObj = jwt.getPayload("id");
if (idObj == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: no user id");
return false;
}
Long userId = Long.valueOf(idObj.toString());
com.bsd.pjcloud.integrated.entity.VisitorUserEntity user = visitorUserService.getById(userId);
if (user == null || "1".equals(user.getStatus())) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: account locked or not exist");
return false;
}
return true;
}
}
@@ -0,0 +1,17 @@
package com.bsd.pjcloud.integrated.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final VisitorTokenInterceptor visitorTokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(visitorTokenInterceptor)
.addPathPatterns("/app/visitor/**");
}
}
@@ -0,0 +1,59 @@
package com.bsd.pjcloud.integrated.controller;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.constant.CacheConstants;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.integrated.entity.CarInfoEntity;
import com.bsd.pjcloud.integrated.entity.VisitorUserEntity;
import com.bsd.pjcloud.integrated.service.VisitorUserService;
import com.bsd.pjcloud.integrated.service.VisitorsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* 访客用户
*
* @author zjf
* @date 2025-07-17 14:58:26
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/app/visitor" )
@Tag(description = "visitorUser" , name = "访客用户管理" )
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
@Inner(value = false)
public class AppVisitorController {
private final VisitorUserService visitorUserService;
@Autowired
private RedisTemplate redisTemplate;
private final VisitorsService visitorsService;
@Operation(summary = "分页查询" , description = "分页查询" )
@GetMapping("/page" )
public R getCarInfoPage(@ParameterObject Page page, @ParameterObject CarInfoEntity carInfo) {
return R.ok(visitorsService.list());
}
}
@@ -3,6 +3,7 @@ package com.bsd.pjcloud.integrated.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -14,7 +15,6 @@ import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.integrated.entity.VisitorUserEntity;
import com.bsd.pjcloud.integrated.service.VisitorUserService;
import io.jsonwebtoken.Jwts;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -24,10 +24,12 @@ import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
@@ -45,39 +47,52 @@ import java.util.concurrent.TimeUnit;
@RequestMapping("/app/visitorUser" )
@Tag(description = "visitorUser" , name = "访客用户管理" )
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
@Inner(value = false)
public class AppVisitorUserController {
private final VisitorUserService visitorUserService;
@Autowired
private RedisTemplate redisTemplate;
@Value("${visitor.jwt-secret}")
private String secret;
// 登录验证
@PostMapping("/login")
// @Inner(value = false)
@Transactional
public R<Map<String, Object>> login(
@RequestParam String phone,
@RequestParam String smsCode,
@RequestParam String randomStr,
@RequestParam String imageCode,
@RequestParam(value = "realName", required = false) String realName
) {
Map<String, Object> response = new HashMap<>();
//0. 验证图片验证码
if (!verifyImageCode(randomStr, imageCode)) {
return R.failed("图片验证码错误或已过期");
}
// 1. 验证短信验证码
if (verifySmsCode(phone,randomStr, smsCode)) {
if (!verifySmsCode(phone, smsCode)) {
return R.failed("短信验证码错误或已过期");
}
// 2. 查找或创建账户
VisitorUserEntity account = visitorUserService.getOne(new LambdaQueryWrapper<VisitorUserEntity>().eq(VisitorUserEntity::getPhone, phone));
account.setLastLogin(LocalDateTime.now());
if (account == null){
account = new VisitorUserEntity();
account.setPhone(phone);
account.setRealName(realName);
account.setStatus("0");
account.setRegisterTime(LocalDateTime.now());
account.setLastLogin(LocalDateTime.now());
visitorUserService.save(account);
}else {
account.setLastLogin(LocalDateTime.now());
// 3. 检查账户状态
if (account.getStatus().equals("1")) {
response.put("code", 4006);
@@ -87,16 +102,14 @@ public class AppVisitorUserController {
visitorUserService.updateById( account);
}
Map<String, Object> stringStringHashMap = new HashMap<>();
stringStringHashMap.put("phone", phone);
stringStringHashMap.put("realName", realName);
stringStringHashMap.put("randomStr", randomStr);
stringStringHashMap.put("smsCode", smsCode);
String token=Jwts.builder().setSubject(account.getId().toString())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7))
.setClaims(stringStringHashMap).compact();
String token = cn.hutool.jwt.JWT.create()
.setPayload("id", account.getId())
.setPayload("phone", account.getPhone())
.setPayload("userType", "visitor")
.setPayload("realName", account.getRealName())
.setPayload("exp", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7)
.setKey(secret.getBytes())
.sign();
response.put("code", 200);
response.put("message", "登录成功");
@@ -105,21 +118,25 @@ public class AppVisitorUserController {
return R.ok(response);
}
private boolean verifySmsCode(String phone, String randomStr, String smsCode) {
String key = CacheConstants.SMS_VISITOR_CODE_KEY + phone + "_" + randomStr;
return !redisTemplate.opsForValue().get(key).equals(smsCode);
}
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
private boolean verifyImageCode(String randomStr, String imageCode) {
String key = CacheConstants.VISITOR_CODE_KEY + randomStr;
Object imageCodeValueObj = redisTemplate.opsForValue().get(key);
if (imageCodeValueObj == null) {
return false;
}
return ip;
String imageCodeValue = imageCodeValueObj.toString();
return imageCodeValue.equals(imageCode);
}
private boolean verifySmsCode(String phone, String smsCode) {
String key = CacheConstants.SMS_VISITOR_CODE_KEY + phone;
Object smsCodeValueObj = redisTemplate.opsForValue().get(key);
if (smsCodeValueObj == null) {
return false;
}
String o = smsCodeValueObj.toString();
return o.equals(smsCode);
}
}
@@ -23,3 +23,5 @@ spring:
# username: root
# password: root
# url: jdbc:mysql://pjcloud-mysql:3306/pjcloudx_integrated?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true
visitor:
jwt-secret: "u8Jk2n3Lk4pQ5r6T7s8V9w0X1y2Z3a4B"