xiongzhu 4 jaren geleden
bovenliggende
commit
c050792fa4

+ 5 - 5
pom.xml

@@ -99,11 +99,6 @@
             <optional>true</optional>
         </dependency>
 
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-data-redis</artifactId>
-        </dependency>
-
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-pool2</artifactId>
@@ -268,6 +263,11 @@
             <version>6.5.4</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+            <version>1.6.2</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 4 - 0
src/main/java/com/izouma/jmrh/Application.java

@@ -4,7 +4,9 @@ package com.izouma.jmrh;
 import cn.licoy.encryptbody.annotation.EnableEncryptBody;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.scheduling.annotation.EnableScheduling;
 import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
 
@@ -12,6 +14,8 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
 @EnableJpaAuditing
 @EnableSwagger2
 @EnableEncryptBody
+@EnableCaching
+@EnableScheduling
 public class Application {
 
     public static void main(String[] args) {

+ 0 - 44
src/main/java/com/izouma/jmrh/config/RedisConfig.java

@@ -1,44 +0,0 @@
-package com.izouma.jmrh.config;
-
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.redis.connection.RedisConnectionFactory;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
-import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
-import org.springframework.data.redis.serializer.StringRedisSerializer;
-
-@Configuration
-@AutoConfigureAfter(RedisAutoConfiguration.class)
-@EnableRedisRepositories
-public class RedisConfig {
-
-    @Bean
-    RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
-
-        RedisTemplate<String, Object> template = new RedisTemplate<>();
-        template.setConnectionFactory(redisConnectionFactory);
-
-        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
-        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class);
-
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
-        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
-        serializer.setObjectMapper(mapper);
-
-        template.setValueSerializer(serializer);
-        //使用StringRedisSerializer来序列化和反序列化redis的key值
-        template.setKeySerializer(new StringRedisSerializer());
-        template.setHashKeySerializer(new StringRedisSerializer());
-        template.setHashValueSerializer(serializer);
-        template.afterPropertiesSet();
-        return template;
-    }
-
-}

+ 23 - 0
src/main/java/com/izouma/jmrh/config/ThreadPoolTaskExecutorConfig.java

@@ -0,0 +1,23 @@
+package com.izouma.jmrh.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+@Configuration
+public class ThreadPoolTaskExecutorConfig {
+
+    @Bean
+    public Executor getAsyncExecutor() {
+
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(10);
+        executor.setMaxPoolSize(100);
+        executor.setQueueCapacity(500);
+        executor.setThreadNamePrefix("TIMCLL-");
+        executor.initialize();
+        return executor;
+    }
+}

+ 1 - 0
src/main/java/com/izouma/jmrh/domain/User.java

@@ -17,6 +17,7 @@ import javax.persistence.*;
 import javax.validation.constraints.Pattern;
 import javax.validation.constraints.Size;
 import java.io.Serializable;
+import java.time.LocalDateTime;
 import java.util.HashSet;
 import java.util.Set;
 

+ 11 - 0
src/main/java/com/izouma/jmrh/dto/Captcha.java

@@ -0,0 +1,11 @@
+package com.izouma.jmrh.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class Captcha {
+    private String key;
+    private String image;
+}

+ 47 - 0
src/main/java/com/izouma/jmrh/service/CaptchaService.java

@@ -0,0 +1,47 @@
+package com.izouma.jmrh.service;
+
+import com.izouma.jmrh.dto.Captcha;
+import com.wf.captcha.SpecCaptcha;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.ehcache.UserManagedCache;
+import org.ehcache.config.builders.ExpiryPolicyBuilder;
+import org.ehcache.config.builders.UserManagedCacheBuilder;
+import org.springframework.stereotype.Service;
+
+import java.awt.*;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.UUID;
+
+@Service
+@AllArgsConstructor
+public class CaptchaService {
+    private final UserManagedCache<String, String> captchaCache =
+            UserManagedCacheBuilder.newUserManagedCacheBuilder(String.class, String.class)
+                    .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10)))
+                    .build(true);
+
+    public Captcha gen() throws IOException, FontFormatException {
+        String key = UUID.randomUUID().toString();
+        SpecCaptcha specCaptcha = new SpecCaptcha(90 * 2, 32 * 2, 5);
+        specCaptcha.setFont(com.wf.captcha.base.Captcha.FONT_7, 24 * 2);
+        String code = specCaptcha.text().toLowerCase();
+        String image = specCaptcha.toBase64();
+        captchaCache.put(key, code);
+        return new Captcha(key, image);
+    }
+
+    public boolean verify(String key, String code) {
+        if (StringUtils.isBlank(key) || StringUtils.isBlank(code)) {
+            return false;
+        }
+        code = code.toLowerCase();
+        boolean verify = false;
+        String trueCode = captchaCache.get(key);
+        if (StringUtils.isNotBlank(trueCode) && trueCode.equals(code)) {
+            verify = true;
+        }
+        return verify;
+    }
+}

+ 25 - 8
src/main/java/com/izouma/jmrh/web/AuthenticationController.java

@@ -2,9 +2,7 @@ package com.izouma.jmrh.web;
 
 import cn.licoy.encryptbody.annotation.encrypt.EncryptBody;
 import cn.licoy.encryptbody.enums.EncryptBodyMethod;
-import com.izouma.jmrh.annotations.OperLog;
 import com.izouma.jmrh.domain.LoginLog;
-import com.izouma.jmrh.domain.MailCode;
 import com.izouma.jmrh.domain.User;
 import com.izouma.jmrh.exception.AuthenticationException;
 import com.izouma.jmrh.exception.BusinessException;
@@ -13,13 +11,12 @@ import com.izouma.jmrh.repo.UserRepo;
 import com.izouma.jmrh.security.JwtTokenUtil;
 import com.izouma.jmrh.security.JwtUserDetailsService;
 import com.izouma.jmrh.security.JwtUserFactory;
-import com.izouma.jmrh.service.LoginLogService;
 import com.izouma.jmrh.service.UserService;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.struts.chain.contexts.ActionContext;
-import org.springframework.http.HttpRequest;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.DisabledException;
@@ -31,11 +28,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
 import java.time.LocalDateTime;
 import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 @Slf4j
 @AllArgsConstructor
@@ -49,6 +46,9 @@ public class AuthenticationController {
     private UserService           userService;
     private LoginLogRepo          loginLogRepo;
     private UserRepo              userRepo;
+    private CacheManager          cacheManager;
+
+    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
 
     @PostMapping("/registerByMail")
     public String registerByMail(@RequestParam String mail, @RequestParam String username, @RequestParam String password) {
@@ -58,6 +58,13 @@ public class AuthenticationController {
 
     @PostMapping("/loginByMail")
     public String loginByMail(String mail, String password) {
+        Cache loginCache = cacheManager.getCache("loginCache");
+        Cache userLockCache = cacheManager.getCache("userLockCache");
+        LocalDateTime lockTime = userLockCache.get(mail, LocalDateTime.class);
+        if (lockTime != null && LocalDateTime.now().isBefore(lockTime)) {
+            throw new BusinessException("密码错误超过3次,请十分钟后再试");
+        }
+
         try {
             User user = userRepo.findByEmail(mail);
             if (user == null) {
@@ -66,6 +73,7 @@ public class AuthenticationController {
             if (!new BCryptPasswordEncoder().matches(password, user.getPassword())) {
                 throw new AuthenticationException("", null);
             }
+
             return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
         } catch (Exception e) {
             log.error("loginByMail", e);
@@ -76,6 +84,15 @@ public class AuthenticationController {
                     .success(false)
                     .time(LocalDateTime.now())
                     .build());
+            loginCache.putIfAbsent(mail, Integer.valueOf(0));
+            loginCache.put(mail, loginCache.get(mail, Integer.class) + 1);
+            if (loginCache.get(mail, Integer.class) >= 3) {
+                userLockCache.put(mail, LocalDateTime.now().plusMinutes(10));
+            }
+            scheduler.schedule(() -> {
+                loginCache.putIfAbsent(mail, Integer.valueOf(0));
+                loginCache.put(mail, loginCache.get(mail, Integer.class) - 1);
+            }, 1, TimeUnit.SECONDS);
             throw new AuthenticationException("用户名或密码错误", e);
         }
     }

+ 30 - 0
src/main/java/com/izouma/jmrh/web/CaptchaController.java

@@ -0,0 +1,30 @@
+package com.izouma.jmrh.web;
+
+import com.izouma.jmrh.dto.Captcha;
+import com.izouma.jmrh.service.CaptchaService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.awt.*;
+import java.io.IOException;
+
+@RestController
+@RequestMapping("/captcha")
+@AllArgsConstructor
+public class CaptchaController {
+
+    private final CaptchaService captchaService;
+
+    @GetMapping("/get")
+    public Captcha get() throws IOException, FontFormatException {
+        return captchaService.gen();
+    }
+
+    @GetMapping("/verify")
+    public boolean verify(@RequestParam String key, @RequestParam String code) {
+        return captchaService.verify(key, code);
+    }
+}

+ 0 - 8
src/main/resources/application.yaml

@@ -34,14 +34,6 @@ spring:
     properties:
       hibernate:
         enable_lazy_load_no_trans: true
-  redis:
-    host: 127.0.0.1
-    port: 6379
-    lettuce:
-      pool:
-        max_active: 8
-        max_idle: 8
-        min_idle: 0
   servlet:
     multipart:
       max_file_size: 100MB