Bläddra i källkod

登录验证添加 验证码

785834757 1 år sedan
förälder
incheckning
c2cb7e83ce

+ 5 - 0
authentication/pom.xml

@@ -58,6 +58,11 @@
             <artifactId>commons</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 15 - 0
authentication/src/main/java/com/jzg/config/JzgAuthenticationProvider.java

@@ -1,5 +1,6 @@
 package com.jzg.config;
 
+import com.jzg.constants.RedisKeyConstants;
 import com.jzg.entity.RedisToken;
 import com.jzg.entity.SysUserRole;
 import com.jzg.entity.SysUser;
@@ -7,6 +8,7 @@ import com.jzg.exception.SystemException;
 import com.jzg.service.SysUserRoleService;
 import com.jzg.service.SysUserService;
 import com.jzg.util.PasswordUtils;
+import org.redisson.api.RedissonClient;
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -16,6 +18,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetailsService;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 public class JzgAuthenticationProvider implements AuthenticationProvider {
@@ -26,6 +29,8 @@ public class JzgAuthenticationProvider implements AuthenticationProvider {
 
     private SysUserRoleService sysUserRoleService;
 
+    private RedissonClient redissonClient;
+
     public JzgAuthenticationProvider(UserDetailsService userDetailsService,SysUserService sysUserService, SysUserRoleService sysUserRoleService) {
         this.userDetailsService = userDetailsService;
         this.sysUserService = sysUserService;
@@ -40,6 +45,16 @@ public class JzgAuthenticationProvider implements AuthenticationProvider {
         String rawPassword = (String) authentication.getCredentials();
         Object details = authentication.getDetails();
         String system = ((JzgUsernamePasswordAuthenticationToken)authentication).getSystem();
+        String captchaId = ((JzgUsernamePasswordAuthenticationToken)authentication).getCaptchaId();
+        String captcha = ((JzgUsernamePasswordAuthenticationToken)authentication).getCaptcha();
+
+        if(Objects.isNull(redissonClient.getBucket(RedisKeyConstants.VERIFICATION_CODE + captchaId))){
+            throw new SystemException("验证码已过期");
+        }
+        String systemCaptcha = redissonClient.getBucket(RedisKeyConstants.VERIFICATION_CODE + captchaId).get().toString();
+        if(!captcha.equals(systemCaptcha)){
+            throw new SystemException("验证码错误,请重新输入");
+        }
 
         SysUser byId = sysUserService.getById(username);
         String salt = byId.getSalt();

+ 28 - 1
authentication/src/main/java/com/jzg/config/JzgUsernamePasswordAuthenticationToken.java

@@ -1,5 +1,6 @@
 package com.jzg.config;
 
+import com.jzg.entity.vo.LoginFormVo;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 
 public class JzgUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
@@ -8,11 +9,30 @@ public class JzgUsernamePasswordAuthenticationToken extends UsernamePasswordAuth
      * 系统标识
      */
     private String system;
-    public JzgUsernamePasswordAuthenticationToken(Object principal, Object credentials,String system) {
+
+    /**
+     * 验证码code
+     */
+    private String captchaId;
+
+    /**
+     * 验证码
+     */
+    private String captcha;
+
+    public JzgUsernamePasswordAuthenticationToken(Object principal, Object credentials,String system,String captchaId,String captcha) {
         super(principal, credentials);
         this.system = system;
+        this.captchaId = captchaId;
+        this.captcha = captcha;
     }
 
+    public JzgUsernamePasswordAuthenticationToken(LoginFormVo loginFormVo){
+        super(loginFormVo.getUserName(),loginFormVo.getPassWord());
+        this.system = loginFormVo.getSystem();
+        this.captchaId = loginFormVo.getCaptchaId();
+        this.captcha = loginFormVo.getCaptcha();
+    }
     public void setSystem(String s){
         this.system = s;
     }
@@ -20,4 +40,11 @@ public class JzgUsernamePasswordAuthenticationToken extends UsernamePasswordAuth
     public String getSystem(){
         return this.system;
     }
+
+    public String getCaptchaId(){
+        return this.captchaId;
+    }
+    public String getCaptcha(){
+        return this.captcha;
+    }
 }

+ 74 - 1
authentication/src/main/java/com/jzg/controller/AuthController.java

@@ -5,17 +5,29 @@ import com.jzg.aop.PremissionCheck;
 import com.jzg.config.JzgTokenService;
 import com.jzg.config.JzgUserDetailsService;
 import com.jzg.config.JzgUsernamePasswordAuthenticationToken;
+import com.jzg.config.RedissonConfig;
+import com.jzg.constants.RedisKeyConstants;
 import com.jzg.entity.SysUser;
 import com.jzg.entity.vo.LoginFormVo;
+import com.jzg.exception.SystemException;
 import com.jzg.handler.JzgAuthenticationSuccessHandler;
 import com.jzg.service.SysUserService;
+import com.wf.captcha.SpecCaptcha;
+import com.wf.captcha.base.Captcha;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.models.annotations.OpenAPI30;
 import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletOutputStream;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.tomcat.util.http.fileupload.IOUtils;
+import org.redisson.api.RedissonClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -23,7 +35,13 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.util.Base64;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 
 @RestController
 @RequestMapping("/auth")
@@ -41,6 +59,9 @@ public class AuthController {
     @Autowired
     private JzgTokenService tokenStore;
 
+    @Autowired
+    RedissonClient redissonClient;
+    private final Logger logger = LoggerFactory.getLogger(AuthController.class);
     /**
      * 登录接口
      * @param loginFormVo
@@ -58,7 +79,7 @@ public class AuthController {
     public void login(@RequestBody LoginFormVo loginFormVo) throws ServletException, IOException {
         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
         HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
-        JzgUsernamePasswordAuthenticationToken authentication = new JzgUsernamePasswordAuthenticationToken(loginFormVo.getUserName(),loginFormVo.getPassWord(),loginFormVo.getSystem());
+        JzgUsernamePasswordAuthenticationToken authentication = new JzgUsernamePasswordAuthenticationToken(loginFormVo);
         Authentication authenticate = authenticationManager.authenticate(authentication);
         SecurityContextHolder.getContext().setAuthentication(authenticate);
         JzgAuthenticationSuccessHandler successHandler = new JzgAuthenticationSuccessHandler(tokenStore);
@@ -66,6 +87,58 @@ public class AuthController {
     }
 
 
+    @GetMapping("captchaById")
+    @Operation(summary = "获取验证码通过用户ID",
+                description = "获取验证码通过用户ID")
+    public void captchaByIdNew(HttpServletResponse response, String captchaId) throws Exception {
+        response.setHeader("Cache-Control", "no-store, no-cache");
+        response.setContentType("image/jpeg");
+        String text;
+        String imageId = null;
+        if(Objects.isNull(redissonClient.getBucket(RedisKeyConstants.VERIFICATION_CODE + captchaId))) {
+            imageId = redissonClient.getBucket(RedisKeyConstants.VERIFICATION_CODE + captchaId).get().toString();
+        }
+
+        Captcha captcha = new SpecCaptcha();
+
+        // 重复验证码问题
+        if (StringUtils.isNotEmpty(imageId)) {
+            text = imageId;
+        } else {
+            // 生成文字验证码
+            text = captcha.text();
+        }
+        // 生成图片验证码
+        BufferedImage image = base64ToBufferedImage(captcha.toBase64());
+        if (StringUtils.isEmpty(captchaId)) {
+            throw new SystemException("captchaId  必传");
+        }
+
+        // 保存到验证码到 redis
+        redissonClient.getBucket(captchaId).set(RedisKeyConstants.VERIFICATION_CODE + text, 120, TimeUnit.SECONDS);
+        ServletOutputStream out = response.getOutputStream();
+        ImageIO.write(image, "jpg", out);
+        IOUtils.closeQuietly(out);
+    }
+
+    public static BufferedImage base64ToBufferedImage(String base64String) {
+        try {
+            // 去除 Base64 编码字符串中的数据前缀,如 "data:image/png;base64,"
+            String base64Image = base64String.split(",")[1];
+            // 解码 Base64 字符串为字节数组
+            byte[] imageBytes = Base64.getDecoder().decode(base64Image);
+            // 将字节数组转换为 ByteArrayInputStream
+            ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes);
+            // 使用 ImageIO 读取输入流并创建 BufferedImage
+            BufferedImage image = ImageIO.read(bis);
+            return image;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
     /**
      * 获取token数据
      * @param token

+ 6 - 0
authentication/src/main/java/com/jzg/entity/vo/LoginFormVo.java

@@ -18,4 +18,10 @@ public class LoginFormVo {
     @Schema(description = "系统code")
     private String system;
 
+    @Schema(description = "验证码id")
+    private String captchaId;
+
+    @Schema(description = "验证码")
+    private String captcha;
+
 }

+ 0 - 8
authentication/src/main/resources/application.yml

@@ -16,13 +16,5 @@ spring:
 mybatis-plus:
   mapper-locations: classpath*:/mapper/*Mapper.xml
 
-logging:
-  charset:
-    console: UTF-8
-  config: classpath:logback.xml
-  file:
-    path: logs/logs.log
-
-
 config:
   sso: 1

+ 3 - 0
commons/src/main/java/com/jzg/constants/RedisKeyConstants.java

@@ -5,4 +5,7 @@ public class RedisKeyConstants {
     //用户角色菜单Key
     public static final String ROLE_MENU_KEY = "ROLE:";
 
+    //验证码Key
+    public static final String VERIFICATION_CODE = "VERIFICATION_CODE:";
+
 }

+ 1 - 1
gateway/src/main/resources/application.yml

@@ -15,4 +15,4 @@ spring:
         - id: platform_route
           uri: http://localhost:8083/
           predicates:
-            - Path=/**
+            - Path=/userRule/**

+ 11 - 1
pom.xml

@@ -25,10 +25,13 @@
     <redisson.version>3.23.3</redisson.version>
     <mysql.connect.version>8.0.26</mysql.connect.version>
     <mybatis.plus.version>3.5.7</mybatis.plus.version>
+    <kaptcha.version>0.0.9</kaptcha.version>
+    <easy.captcha.version>1.6.2</easy.captcha.version>
   </properties>
 
 
   <dependencies>
+
     <!-- Spring Boot Starter Web -->
     <dependency>
       <groupId>org.springframework.boot</groupId>
@@ -97,7 +100,7 @@
       <dependency>
         <groupId>org.springframework.statemachine</groupId>
         <artifactId>spring-statemachine-core</artifactId>
-        <version>3.1.0</version> <!-- 你可以选择适合你的版本 -->
+        <version>4.0.0</version> <!-- 你可以选择适合你的版本 -->
       </dependency>
 
       <!-- 数据库驱动,如 MySQL -->
@@ -126,6 +129,13 @@
         <version>${mybatis.plus.version}</version>
       </dependency>
 
+      <!-- kaptcha -->
+      <dependency>
+        <groupId>com.github.whvcse</groupId>
+        <artifactId>easy-captcha</artifactId>
+        <version>${easy.captcha.version}</version>
+      </dependency>
+
     </dependencies>
   </dependencyManagement>