fengjiajia 1 month ago
parent
commit
429c289c6f

+ 11 - 0
pom.xml

@@ -87,6 +87,17 @@
             <version>2.8.9</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.hibernate.validator</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <version>6.2.0.Final</version>
+        </dependency>
+        <!-- Redisson -->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+            <version>3.18.0</version>
+        </dependency>
 
 
     </dependencies>

+ 1 - 0
src/main/java/com/zhentao/TourismHdApplication.java

@@ -4,6 +4,7 @@ import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 
+
 @SpringBootApplication
 @MapperScan("com.zhentao.mapper")
 public class TourismHdApplication {

+ 43 - 0
src/main/java/com/zhentao/config/RedissonConfig.java

@@ -0,0 +1,43 @@
+package com.zhentao.config;
+
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+// 定义这是一个Spring配置类,用于配置Redisson相关的Bean
+@Configuration
+public class RedissonConfig {
+//     从配置文件(如application.properties或application.yml)中获取Redis服务器的主机地址
+    @Value("${spring.redis.host}")
+    private String host;
+    // 从配置文件中获取Redis服务器的端口号
+    @Value("${spring.redis.port}")
+    private int port;
+    // 从配置文件中获取要使用的Redis数据库索引
+    @Value("${spring.redis.database}")
+    private int database;
+    // 定义一个Bean,名称为getRedisson,返回一个RedissonClient实例,Spring会在需要RedissonClient的地方注入这个实例
+    @Bean
+    public RedissonClient getRedisson(){
+
+        // 创建一个Redisson的配置对象z
+        Config config = new Config();
+
+        // 配置Redisson使用单机模式,并设置Redis服务器的地址
+        config.useSingleServer().setAddress("redis://"+host+":"+port)
+
+                // 设置要使用的Redis数据库索引
+                .setDatabase(database)
+                ;
+
+
+        // 设置锁的看门狗超时时间为2000毫秒。看门狗用于自动延长锁的持有时间,防止业务执行时间过长导致锁提前释放
+        config.setLockWatchdogTimeout(2000);
+
+        // 根据配置创建并返回RedissonClient实例
+        return Redisson.create(config);
+    }
+}

+ 51 - 0
src/main/java/com/zhentao/controller/UserController.java

@@ -0,0 +1,51 @@
+package com.zhentao.controller;
+
+import com.zhentao.dto.user.NoteDto;
+import com.zhentao.dto.user.UserLoginDto;
+import com.zhentao.dto.user.UserPassDto;
+import com.zhentao.dto.user.UserRegister;
+import com.zhentao.service.UserLoginService;
+import com.zhentao.vo.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+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 javax.validation.Valid;
+
+
+@RestController
+@RequestMapping("user")
+public class UserController {
+
+    @Autowired
+    public UserLoginService userLoginService;
+
+    //注册
+    @PostMapping("/register")
+    public Result Register(@RequestBody @Valid UserRegister userRegister){
+        return userLoginService.register(userRegister);
+    }
+
+    //验证码
+    @PostMapping("/code")
+    public Result code(@RequestBody @Valid NoteDto noteDto) {
+//        System.err.println(noteDto);
+        return userLoginService.note(noteDto);
+    }
+
+    //手机号登录
+    @PostMapping("/login")
+    public Result login(@RequestBody @Valid UserLoginDto userLoginDto) {
+        return userLoginService.login(userLoginDto);
+    }
+
+    //账号和密码进行登录
+    @PostMapping("/UserPassLogin")
+    public Result UserPassLogin(@RequestBody @Valid UserPassDto userPassDto) {
+        return userLoginService.UserPassLogin(userPassDto);
+
+    }
+}

+ 19 - 0
src/main/java/com/zhentao/dto/user/NoteDto.java

@@ -0,0 +1,19 @@
+package com.zhentao.dto.user;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+
+@Data
+public class NoteDto {
+
+    @NotBlank(message = "手机号不能为空")
+    @Pattern(regexp = "^1[3-9]\\d{9}$",message = "手机号格式不正确" )
+    private String phone;
+
+
+
+
+
+}

+ 18 - 0
src/main/java/com/zhentao/dto/user/UserLoginDto.java

@@ -0,0 +1,18 @@
+package com.zhentao.dto.user;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+
+@Data
+public class UserLoginDto {
+
+    @NotBlank(message = "手机号不能为空")
+    @Pattern(regexp = "^1[3-9]\\d{9}$",message = "手机号格式不正确" )
+    private String phone;
+
+    //验证码
+    @NotBlank(message = "验证码不能为空")
+    private String code;
+}

+ 14 - 0
src/main/java/com/zhentao/dto/user/UserPassDto.java

@@ -0,0 +1,14 @@
+package com.zhentao.dto.user;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class UserPassDto {
+
+    @NotBlank(message = "账号不能为空")
+    private String username;
+    @NotBlank(message = "密码不能为空")
+    private String password;
+}

+ 26 - 0
src/main/java/com/zhentao/dto/user/UserRegister.java

@@ -0,0 +1,26 @@
+package com.zhentao.dto.user;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+
+@Data
+public class UserRegister {
+    //手机号
+    @NotBlank(message = "手机号不能为空")
+    @Pattern(regexp = "^1[3-9]\\d{9}$",message = "手机号格式不正确" )
+    private String phone;
+
+    //验证码
+    @NotBlank(message = "验证码不能为空")
+    private String code;
+
+    //用户名
+    @NotBlank(message = "用户名不能为空")
+    private String username;
+
+    //密码
+    @NotBlank(message = "密码不能为空")
+    private String password;
+}

+ 4 - 0
src/main/java/com/zhentao/service/PropsService.java

@@ -0,0 +1,4 @@
+package com.zhentao.service;
+
+public interface PropsService {
+}

+ 18 - 0
src/main/java/com/zhentao/service/UserLoginService.java

@@ -2,6 +2,14 @@ package com.zhentao.service;
 
 import com.zhentao.domain.UserLogin;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhentao.dto.user.NoteDto;
+import com.zhentao.dto.user.UserLoginDto;
+import com.zhentao.dto.user.UserPassDto;
+import com.zhentao.dto.user.UserRegister;
+import com.zhentao.vo.Result;
+import org.apache.tomcat.jni.User;
+
+import java.io.InputStream;
 
 /**
 * @author lenovo
@@ -10,4 +18,14 @@ import com.baomidou.mybatisplus.extension.service.IService;
 */
 public interface UserLoginService extends IService<UserLogin> {
 
+    //注册
+    Result  register(UserRegister userRegister);
+    //验证码
+    Result note(NoteDto noteDto);
+
+    //手机号登录
+    Result login(UserLoginDto userLoginDto);
+
+    //账号密码登录
+    Result UserPassLogin(UserPassDto userPassDto);
 }

+ 9 - 0
src/main/java/com/zhentao/service/impl/PropsServiceimpl.java

@@ -0,0 +1,9 @@
+package com.zhentao.service.impl;
+
+import com.zhentao.service.PropsService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PropsServiceimpl implements PropsService {
+
+}

+ 213 - 0
src/main/java/com/zhentao/service/impl/UserLoginServiceImpl.java

@@ -1,10 +1,30 @@
 package com.zhentao.service.impl;
 
+import cn.hutool.core.util.IdUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zhentao.domain.UserLogin;
+import com.zhentao.dto.user.NoteDto;
+import com.zhentao.dto.user.UserLoginDto;
+import com.zhentao.dto.user.UserPassDto;
+import com.zhentao.dto.user.UserRegister;
+import com.zhentao.enums.ApiServerException;
+import com.zhentao.exception.AsynException;
 import com.zhentao.service.UserLoginService;
 import com.zhentao.mapper.UserLoginMapper;
+import com.zhentao.tool.TokenUtils;
+import com.zhentao.vo.Result;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
+import org.springframework.util.DigestUtils;
+
+
+import java.io.InputStream;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 /**
 * @author lenovo
@@ -15,6 +35,199 @@ import org.springframework.stereotype.Service;
 public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, UserLogin>
     implements UserLoginService{
 
+    @Autowired
+    private UserLoginMapper userLoginMapper;
+    @Autowired
+    private RedissonClient redissonClient;
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+    //注册
+    @Override
+    public Result register(UserRegister userRegister) {
+        //打印用户注册的信息
+        System.err.println(userRegister);
+        //使用redisson客户端获取分布式锁,确保并发情况下用户注册的安全性
+        RLock lock = redissonClient.getLock(userRegister.getPhone() + userRegister.getPassword());
+        try {
+            boolean b = lock.tryLock(10, 20, TimeUnit.SECONDS);
+
+            if(b){
+                //用来判断验证码是否正确
+                String s = stringRedisTemplate.opsForValue().get(userRegister.getPhone());
+                System.err.println("redis取出来的验证码"+s);
+
+                //验证码不匹配就抛出异常
+                if(!s.equals(userRegister.getCode())){
+                    throw new AsynException(ApiServerException.NOTE_ERROR);
+                }
+                //根据手机号查询信息
+                QueryWrapper<UserLogin> queryWrapper=new QueryWrapper<>();
+                queryWrapper.eq("user_mobile",userRegister.getPhone());
+                UserLogin one = this.getOne(queryWrapper);
+
+                if(one==null){
+                    //新用户注册的流程
+                    UserLogin userLogin=new UserLogin();
+                    userLogin.setUserMobile(userRegister.getPhone());
+                    userLogin.setUserUsername(userRegister.getUsername());
+                    //随机字符串
+                    String uuid = String.valueOf(UUID.randomUUID());
+                    userLogin.setSalt(uuid);
+
+                    //md5加密
+                    String s1 = DigestUtils.md5DigestAsHex((uuid + userRegister.getPassword()).getBytes());
+                    userLogin.setUserPassword(s1);
+
+                    //生成唯一Id
+                    long l = IdUtil.getSnowflake(1, 1).nextId();
+                    //进行注册
+                    boolean save = this.save(userLogin);
+                    if(save){
+                        return Result.OK(save,"注册成功");
+                    }else{
+                        return Result.ERR(save,"注册失败");
+                    }
+                }else{
+                    //老用户更新信息流程
+                    one.setUserUsername(userRegister.getUsername());
+                    //随机字符串
+                    String uuid = String.valueOf(UUID.randomUUID());
+                    one.setSalt(uuid);
+                    //md5加密
+                    String s1 = DigestUtils.md5DigestAsHex((uuid + userRegister.getPassword()).getBytes());
+                    one.setUserPassword(s1);
+
+                    //进行更新
+                    boolean b1 = this.updateById(one);
+                    if(b1){
+                        return Result.OK(b1,"注册成功");
+                    }else{
+                        return Result.ERR(b1,"注册失败");
+                    }
+                }
+            }
+
+        }catch (InterruptedException e){
+            Thread.currentThread().interrupt();
+        }finally {
+            //释放锁
+            lock.unlock();
+        }
+        return null;
+    }
+
+
+    //验证码
+    @Override
+    public Result note(NoteDto noteDto) {
+        //随机生成六位数
+        int randomSixDigit=100000 + (int)(Math.random() * 900000);
+        System.err.println("手机号:"+noteDto.getPhone());
+        System.err.println("验证码:"+randomSixDigit);
+        stringRedisTemplate.opsForValue().set(noteDto.getPhone(),randomSixDigit+"");
+
+        return Result.OK(randomSixDigit,"发送成功");
+    }
+    //登录
+    /**
+     * 用户登录方法
+     * 使用Redis分布式锁来防止并发登录
+     * @param userLoginDto 用户登录信息,包含手机号和验证码
+     * @return 登录结果,包括token和提示信息
+     */
+    @Override
+    public Result login(UserLoginDto userLoginDto) {
+        // 获取Redis锁,锁的键为用户手机号,防止并发登录
+        RLock lock = redissonClient.getLock(userLoginDto.getPhone() + "phone");
+        try {
+            // 尝试获取锁,等待时间10秒,锁的过期时间为20秒
+            boolean b = lock.tryLock(10, 20, TimeUnit.SECONDS);
+            if (b) {
+                // 打印用户登录信息,用于调试
+                System.err.println(userLoginDto);
+                // 从Redis中获取验证码
+                String s = stringRedisTemplate.opsForValue().get(userLoginDto.getPhone());
+                // 打印从Redis中获取的验证码,用于调试
+                System.err.println("redis取出来的验证码" + s);
+                // 验证码不匹配则抛出异常
+                if (!s.equals(userLoginDto.getCode())) {
+                    throw new AsynException(ApiServerException.NOTE_ERROR);
+                }
+                try {
+                    // 查询数据库中是否存在该用户
+                    QueryWrapper<UserLogin> queryWrapper = new QueryWrapper<>();
+                    queryWrapper.eq("user_mobile", userLoginDto.getPhone());
+                    UserLogin one = this.getOne(queryWrapper);
+                    // 生成JWT token
+                    String token = TokenUtils.createJwtToken(one.getId() + "");
+                    // 返回登录成功结果,包含token
+                    return Result.OK(token, "登录成功");
+                } catch (NullPointerException e) {
+                    // 用户不存在时返回错误结果
+                    return Result.ERR("登录失败", "用户不存在");
+                }
+            }
+        } catch (InterruptedException e) {
+            // 中断当前线程,重新抛出自定义异常
+            Thread.currentThread().interrupt();
+            throw new AsynException(ApiServerException.NOTE_ERROR);
+        } finally {
+            // 释放锁
+            lock.unlock();
+        }
+        return null;
+    }
+    //账号密码登录
+    /**
+     * 使用用户名和密码进行用户登录的方法
+     * 该方法实现了用户认证和JWT令牌的生成
+     *
+     * @param userPassDto 包含用户名和密码的DTO对象
+     * @return 登录结果,包括登录状态和JWT令牌
+     */
+    @Override
+    public Result UserPassLogin(UserPassDto userPassDto) {
+        // 获取Redisson客户端的锁对象,用于处理并发登录请求
+        RLock lock = redissonClient.getLock(userPassDto.getUsername());
+        try {
+            // 尝试获取锁,设置等待和持有时间
+            boolean b = lock.tryLock(10, 20, TimeUnit.SECONDS);
+            if (b){
+                // 查询用户信息,根据用户名
+                QueryWrapper<UserLogin> queryWrapper = new QueryWrapper<>();
+                queryWrapper.eq("user_username",userPassDto.getUsername());
+                UserLogin one = this.getOne(queryWrapper);
+                // 如果用户不存在,抛出异常
+                if (one==null){
+                    throw new AsynException(ApiServerException.NULL_USERNAME);
+                }
+                // 获取用户盐值,用于密码加密
+                String salt = one.getSalt();
+                // 加密用户输入的密码,并与数据库中的密码进行比较
+                String s = DigestUtils.md5DigestAsHex((salt + userPassDto.getPassword()).getBytes());
+                if (!s.equals(one.getUserPassword())){
+                    throw new AsynException(ApiServerException.NULL_PASSWORD);
+                }
+                // 生成JWT令牌
+                String jwtToken = TokenUtils.createJwtToken(one.getId()+"");
+                // 返回登录成功结果和JWT令牌
+                return Result.OK("登录成功",jwtToken);
+            }else {
+                // 如果获取锁超时,返回错误信息
+                return Result.ERR("获取锁超时",null);
+            }
+        }catch (InterruptedException e){
+            // 如果线程被中断,恢复中断状态,并返回错误信息
+            Thread.currentThread().interrupt();
+            return Result.ERR("线程被中断",null);
+        }finally {
+            // 释放锁
+            lock.unlock();
+        }
+    }
+
+
 }
 
 

+ 2 - 2
src/main/java/com/zhentao/tool/WechatLoginUtil.java

@@ -19,8 +19,8 @@ import java.util.Base64;
 
 public class WechatLoginUtil {
 
-    private static final String APP_ID = "wxf1e39756b564fd41"; // 替换为你的AppID
-    private static final String APP_SECRET = "628082cc4dc055f61be83dffa6761e55"; // 替换为你的AppSecret
+    private static final String APP_ID = "wx97b71d70f5b2d51f"; // 替换为你的AppID
+    private static final String APP_SECRET = "b78d9bf60e17518475bc8e1d44fd9ad6"; // 替换为你的AppSecret
     private static final String WECHAT_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";
 
     public static WeChatSessionModel getAuthInfo(String code) throws IOException {

+ 2 - 9
src/main/resources/application.yml

@@ -3,18 +3,11 @@ server:
 spring:
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://47.110.46.22:3306/lvYou?useSSL=false&serverTimezone=UTC
+    url: jdbc:mysql://47.110.46.22/lvYou?useSSL=false&serverTimezone=UTC
     username: root
     password: Fengjaijia0610
   redis:
     host: 47.110.46.22
     port: 6379
     database: 0
-jwt:
-  header: Authorization
-  tokenPrefix: Bearer
-  secret: "maohe101"
-  expireTime: 3600000 #token有效时间 300000 ==>60分钟
-#微信小程序的配置
-  APPID: wx97b71d70f5b2d51f
-  APPSECRET: b78d9bf60e17518475bc8e1d44fd9ad6
+