wuhongshuang 3 年 前
コミット
9e4981da21

+ 11 - 6
pom.xml

@@ -52,11 +52,6 @@
 				<artifactId>mybatis-plus-generator</artifactId>
 				<version>${mybatis-plus.version}</version>
 			</dependency>
-<!--			<dependency>-->
-<!--				<groupId>org.mybatis.spring.boot</groupId>-->
-<!--				<artifactId>mybatis-spring-boot-starter</artifactId>-->
-<!--				<version>${mybatis-spring-boot-starter.version}</version>-->
-<!--			</dependency>-->
 		</dependencies>
 	</dependencyManagement>
 
@@ -198,6 +193,16 @@
 			<artifactId>EsBaseServer</artifactId>
 			<version>1.2.10</version>
 		</dependency>
+		<!--redis start-->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-pool2</artifactId>
+		</dependency>
+		<!--redis end-->
 	</dependencies>
 
 	<build>
@@ -219,7 +224,7 @@
 		</repository>
 		<repository>
 			<id>nexus</id>
-			<url>http://www.redouble.store:1888/repository/maven-public/</url>
+			<url>http://www.redouble.store:1888/repository/crbank-host/</url>
 			<releases>
 				<enabled>true</enabled>
 			</releases>

+ 10 - 0
src/main/java/com/szwl/constant/ConfigConsts.java

@@ -5,4 +5,14 @@ package com.szwl.constant;
  */
 public class ConfigConsts {
     public static final String TEST = "TEST";
+    /**
+     * token
+     * 请求头:Authorization
+     */
+    public static final String AUTHORIZATION = "Authorization";
+
+    /**
+     * token时效性
+     */
+    public static final Integer TOKEN_EXPIRE_MINUTE = 30;
 }

+ 26 - 15
src/main/java/com/szwl/controller/TAdminController.java

@@ -1,11 +1,17 @@
 package com.szwl.controller;
 
 
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.lang.UUID;
+import cn.hutool.core.util.IdUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.szwl.constant.ResponseCodesEnum;
+import com.szwl.exception.BizException;
+import com.szwl.manager.TokenManager;
 import com.szwl.model.bo.R;
 import com.szwl.model.bo.ResponseModel;
+import com.szwl.model.bo.UserDetailBO;
 import com.szwl.model.entity.TAdmin;
 import com.szwl.model.entity.TMessageCode;
 import com.szwl.service.TAdminService;
@@ -19,6 +25,7 @@ import org.springframework.web.bind.annotation.*;
 
 import java.util.Date;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * <p>
@@ -33,6 +40,8 @@ import java.util.List;
 @RequestMapping("/tAdmin")
 public class TAdminController {
     @Autowired
+    TokenManager tokenManager;
+    @Autowired
     TAdminService tAdminService;
     @Autowired
     TMessageCodeService tMessageCodeService;
@@ -108,27 +117,29 @@ public class TAdminController {
     }
     @ApiOperation(value = "登录")
     @PostMapping("/login")
-    public ResponseModel<?> login(String username,String password) {
+    public ResponseModel<UserDetailBO> login(String username, String password) {
         if(StringUtils.isEmpty(username)||StringUtils.isEmpty(password)){
             return R.fail(ResponseCodesEnum.A0001,"参数有空");
         }
+
         //验证用户名登录
         LambdaQueryWrapper<TAdmin> query = Wrappers.lambdaQuery();
-        query.eq(TAdmin::getUsername,username);
+//        query.eq(TAdmin::getUsername,username);
         query.eq(TAdmin::getPassword,password);
-        List<TAdmin> list = tAdminService.list(query);
-        if(list.size()>0){
-            return R.ok(list.get(0));
-        }
-        //验证手机登录
-        LambdaQueryWrapper<TAdmin> query1 = Wrappers.lambdaQuery();
-        query1.eq(TAdmin::getPhone,username);
-        query1.eq(TAdmin::getPassword,password);
-        List<TAdmin> list1 = tAdminService.list(query1);
-        if(list1.size()>0){
-            return R.ok(list1.get(0));
-        }
-        return R.fail(ResponseCodesEnum.A0001,"密码或用户名错误");
+        query.and(
+                wrapper -> {
+                    wrapper.eq(TAdmin::getUsername,username)
+                            .or().eq(TAdmin::getPhone,username);
+                }
+        );
+        TAdmin tAdmin = Optional.ofNullable(tAdminService.getOnly(query))
+                .orElseThrow(() -> new BizException(ResponseCodesEnum.L0002));
+        UserDetailBO userDetailBO = BeanUtil.copyProperties(tAdmin,UserDetailBO.class);
+        String token = IdUtil.simpleUUID();
+        userDetailBO.setCurrentToken(token);
+        // 保存到redis
+        tokenManager.saveAuthentication(token,userDetailBO);
+        return R.ok(userDetailBO);
     }
     @ApiOperation(value = "修改密码")
     @PostMapping("/updatePassword")

+ 64 - 0
src/main/java/com/szwl/filter/TokenFilter.java

@@ -0,0 +1,64 @@
+package com.szwl.filter;
+
+import com.szwl.constant.ResponseCodesEnum;
+import com.szwl.manager.TokenManager;
+import com.szwl.model.bo.R;
+import com.szwl.util.ServletUtil;
+import com.szwl.util.UrlUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author PT-ZHOUYUHENG
+ * @date 2021-11-01 15:59
+ */
+@Component
+@Slf4j
+public class TokenFilter extends OncePerRequestFilter {
+
+    @Autowired
+    private TokenManager tokenManager;
+
+    @Value("${permitAll.url:[]}")
+    private String[] permitAll;
+
+    /**
+     * token不能为空
+     *
+     * @param httpServletRequest
+     * @param httpServletResponse
+     * @param filterChain
+     * @throws ServletException
+     * @throws IOException
+     */
+    @Override
+    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
+        String uri = httpServletRequest.getRequestURI();
+        log.info("--request uri: {}", uri);
+        //如果是忽略token的放行
+        if ( UrlUtil.matches(uri, permitAll)) {
+            log.info("--the request uri is ignore token");
+            filterChain.doFilter(httpServletRequest, httpServletResponse);
+            return;
+        }
+        //校验 token 是否合法
+        if(tokenManager.checkToken(httpServletRequest)){
+            filterChain.doFilter(httpServletRequest, httpServletResponse);
+            return;
+        }
+        //响应错误
+        log.info("--the request valid fail");
+        ServletUtil.renderJSON(httpServletResponse, R.fail(ResponseCodesEnum.L0005));
+    }
+}

+ 96 - 0
src/main/java/com/szwl/manager/TokenManager.java

@@ -0,0 +1,96 @@
+package com.szwl.manager;
+
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.szwl.constant.ConfigConsts;
+import com.szwl.model.bo.UserDetailBO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.concurrent.TimeUnit;
+@Slf4j
+@Component
+public class TokenManager {
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    /**
+     * 校验token
+     * @param request
+     * @return
+     */
+    public boolean checkToken(HttpServletRequest request){
+        String token = request.getHeader(ConfigConsts.AUTHORIZATION);
+        if(StrUtil.isEmpty(token)){
+            return false;
+        }
+        UserDetailBO details = getUserDetails(token);
+        if (null!= details) {
+            return true;
+        }
+        //登陆凭证已过期或不可用
+        log.info("--token {} is expired", token);
+        return false;
+    }
+    /**
+     * 根据token查找认证信息
+     *
+     * @param token
+     * @return
+     */
+    public UserDetailBO getUserDetails(String token) {
+        String val = redisTemplate.opsForValue().get(token);
+        if (StringUtils.isNotEmpty(val)) {
+            try {
+                UserDetailBO details = JSON.parseObject(val, UserDetailBO.class);
+                return details;
+            } catch (Exception e) {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 保存用户信息
+     *
+     * @param token
+     * @param userDetails
+     */
+    public void saveAuthentication(String token, UserDetailBO userDetails) {
+        saveAuthentication(token, userDetails, ConfigConsts.TOKEN_EXPIRE_MINUTE, TimeUnit.MINUTES);
+    }
+
+    /**
+     * 保存用户信息
+     *
+     * @param token
+     * @param userDetails
+     */
+    public void saveAuthentication(String token, UserDetailBO userDetails, int timeout, TimeUnit timeUnit) {
+        userDetails.setCurrentToken(token);
+        redisTemplate.opsForValue().set(token, JSON.toJSONString(userDetails), timeout, timeUnit);
+    }
+
+    /**
+     * 删除用户认证信息
+     *
+     * @param token
+     */
+    public void deleteAuthentication(String token) {
+        redisTemplate.delete(token);
+    }
+
+    /**
+     * 更新用户认证的时效,防止一直操作但token过期
+     *
+     * @param token
+     */
+    public void updateAuthenticationExpire(String token) {
+        redisTemplate.expire(token, ConfigConsts.TOKEN_EXPIRE_MINUTE, TimeUnit.MINUTES);
+    }
+}

+ 17 - 0
src/main/java/com/szwl/model/bo/UserDetailBO.java

@@ -0,0 +1,17 @@
+package com.szwl.model.bo;
+
+import com.szwl.model.entity.TAdmin;
+import lombok.Data;
+
+@Data
+public class UserDetailBO extends TAdmin {
+    /**
+     * 当前IP地址
+     */
+    private String currentIp;
+
+    /**
+     * 当前token
+     */
+    private String currentToken;
+}

+ 227 - 0
src/main/java/com/szwl/util/ServletUtil.java

@@ -0,0 +1,227 @@
+package com.szwl.util;
+
+import com.alibaba.fastjson.JSON;
+import org.springframework.http.MediaType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.URLEncoder;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author wuhs
+ * @date 2021-11-01 14:13
+ */
+public class ServletUtil {
+
+    /**
+     * 组装下载文件请求体,防止中文乱码
+     *
+     * @param response
+     * @param fileName
+     * @return
+     * @throws UnsupportedEncodingException
+     */
+    public static HttpServletResponse buildDownloadHeader(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
+        response.reset();
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+        //防止中文乱码
+        String percentEncodedFileName = percentEncode(fileName);
+        StringBuilder contentDispositionValue = new StringBuilder();
+        contentDispositionValue.append("attachment; filename=")
+                .append(percentEncodedFileName)
+                .append(";")
+                .append("filename*=")
+                .append("utf-8''")
+                .append(percentEncodedFileName);
+        response.setHeader("Content-disposition", contentDispositionValue.toString());
+        return response;
+    }
+
+    /**
+     * 百分号编码工具方法
+     *
+     * @param s 需要百分号编码的字符串
+     * @return 百分号编码后的字符串
+     */
+    private static String percentEncode(String s) throws UnsupportedEncodingException {
+        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
+        return encode.replaceAll("\\+", "%20");
+    }
+
+    /**
+     * 以json方式渲染到前端
+     *
+     * @param response
+     * @param value
+     */
+    public static void renderJSON(HttpServletResponse response, Object value) throws IOException {
+        response.setContentType("application/json;charset=UTF-8");
+        response.getWriter().write(JSON.toJSONString(value));
+        response.getWriter().flush();
+        response.getWriter().close();
+    }
+
+    /**
+     * 获取IP方法
+     *
+     * @author ruoyi
+     */
+    public static String getIpAddr(HttpServletRequest request) {
+        if (request == null) {
+            return "unknown";
+        }
+        String ip = request.getHeader("x-forwarded-for");
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("X-Forwarded-For");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("X-Real-IP");
+        }
+
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+
+        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
+    }
+
+    public static boolean internalIp(String ip) {
+        byte[] addr = textToNumericFormatV4(ip);
+        return internalIp(addr) || "127.0.0.1".equals(ip);
+    }
+
+    private static boolean internalIp(byte[] addr) {
+        if (addr == null || addr.length < 2) {
+            return true;
+        }
+        final byte b0 = addr[0];
+        final byte b1 = addr[1];
+        // 10.x.x.x/8
+        final byte SECTION_1 = 0x0A;
+        // 172.16.x.x/12
+        final byte SECTION_2 = (byte) 0xAC;
+        final byte SECTION_3 = (byte) 0x10;
+        final byte SECTION_4 = (byte) 0x1F;
+        // 192.168.x.x/16
+        final byte SECTION_5 = (byte) 0xC0;
+        final byte SECTION_6 = (byte) 0xA8;
+        switch (b0) {
+            case SECTION_1:
+                return true;
+            case SECTION_2:
+                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
+                    return true;
+                }
+            case SECTION_5:
+                switch (b1) {
+                    case SECTION_6:
+                        return true;
+                }
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * 将IPv4地址转换成字节
+     *
+     * @param text IPv4地址
+     * @return byte 字节
+     */
+    public static byte[] textToNumericFormatV4(String text) {
+        if (text.length() == 0) {
+            return null;
+        }
+
+        byte[] bytes = new byte[4];
+        String[] elements = text.split("\\.", -1);
+        try {
+            long l;
+            int i;
+            switch (elements.length) {
+                case 1:
+                    l = Long.parseLong(elements[0]);
+                    if ((l < 0L) || (l > 4294967295L)) {
+                        return null;
+                    }
+                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
+                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
+                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 2:
+                    l = Integer.parseInt(elements[0]);
+                    if ((l < 0L) || (l > 255L)) {
+                        return null;
+                    }
+                    bytes[0] = (byte) (int) (l & 0xFF);
+                    l = Integer.parseInt(elements[1]);
+                    if ((l < 0L) || (l > 16777215L)) {
+                        return null;
+                    }
+                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
+                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 3:
+                    for (i = 0; i < 2; ++i) {
+                        l = Integer.parseInt(elements[i]);
+                        if ((l < 0L) || (l > 255L)) {
+                            return null;
+                        }
+                        bytes[i] = (byte) (int) (l & 0xFF);
+                    }
+                    l = Integer.parseInt(elements[2]);
+                    if ((l < 0L) || (l > 65535L)) {
+                        return null;
+                    }
+                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 4:
+                    for (i = 0; i < 4; ++i) {
+                        l = Integer.parseInt(elements[i]);
+                        if ((l < 0L) || (l > 255L)) {
+                            return null;
+                        }
+                        bytes[i] = (byte) (int) (l & 0xFF);
+                    }
+                    break;
+                default:
+                    return null;
+            }
+        } catch (NumberFormatException e) {
+            return null;
+        }
+        return bytes;
+    }
+
+    public static String getHostIp() {
+        try {
+            return InetAddress.getLocalHost().getHostAddress();
+        } catch (UnknownHostException e) {
+        }
+        return "127.0.0.1";
+    }
+
+    public static String getHostName() {
+        try {
+            return InetAddress.getLocalHost().getHostName();
+        } catch (UnknownHostException e) {
+        }
+        return "未知";
+    }
+
+}

+ 66 - 0
src/main/java/com/szwl/util/UrlUtil.java

@@ -0,0 +1,66 @@
+package com.szwl.util;
+
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Collection;
+
+/**
+ * URL工具类
+ *
+ * @author wuhs
+ * @date 2021-11-02 17:58
+ */
+public class UrlUtil {
+
+    /**
+     * url匹配
+     *
+     * @param url
+     * @param patterns
+     * @return
+     */
+    public static boolean matches(String url, Collection<String> patterns) {
+        if (CollectionUtils.isEmpty(patterns)) {
+            return false;
+        }
+        for (String pattern : patterns) {
+            if (matches(url, pattern)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * url匹配
+     *
+     * @param url
+     * @param patterns
+     * @return
+     */
+    public static boolean matches(String url, String[] patterns) {
+        if (patterns == null || patterns.length == 0) {
+            return false;
+        }
+        for (String pattern : patterns) {
+            if (matches(url, pattern)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * url匹配
+     *
+     * @param url
+     * @param pattern
+     * @return
+     */
+    public static boolean matches(String url, String pattern) {
+        AntPathMatcher matcher = new AntPathMatcher();
+        return matcher.match(pattern, url);
+    }
+
+}

+ 11 - 1
src/main/resources/bootstrap.yml

@@ -52,6 +52,15 @@ spring:
     max-concurrency: 2000
     listener.simple.concurrency: 1000
     listener.simple.max-concurrency: 2000
+
+  redis:
+    timeout: 5000
+    cluster:
+      nodes: 120.78.140.173:7000,120.78.140.173:7001,47.112.127.131:7001,47.112.127.131:7000,112.74.63.148:7001,112.74.63.148:7000
+      max-attempts: 5
+      max-redirects: 7
+      timeout: 12000
+
 elasticSearch:
   host: 120.78.140.173
 #elasticSearch.host: 10.0.0.155
@@ -82,4 +91,5 @@ mybatis-plus:
 
 logging:
   level:
-    com.szwl: debug
+    com.szwl: debug
+