Просмотр исходного кода

自定义拦截器 微信一键授权登录

bihuisong 5 месяцев назад
Родитель
Сommit
762675e8b8
30 измененных файлов с 1224 добавлено и 358 удалено
  1. 5 0
      qmjszx-admin/pom.xml
  2. 45 0
      qmjszx-admin/src/main/java/beilv/web/controller/system/AppAuthController.java
  3. 9 0
      qmjszx-admin/src/main/resources/application.yml
  4. 25 0
      qmjszx-common/pom.xml
  5. 19 0
      qmjszx-common/src/main/java/beilv/common/enums/ErrorCodeConstants.java
  6. 31 0
      qmjszx-common/src/main/java/beilv/common/enums/LoginTypeEnum.java
  7. 144 0
      qmjszx-common/src/main/java/beilv/common/json/JsonUtils.java
  8. 27 0
      qmjszx-common/src/main/java/beilv/common/logger/LoginLogTypeEnum.java
  9. 110 0
      qmjszx-common/src/main/java/beilv/common/servlet/ServletUtils.java
  10. 28 0
      qmjszx-common/src/main/java/beilv/common/utils/ErrorCode.java
  11. 70 24
      qmjszx-common/src/main/java/beilv/common/utils/ExceptionUtil.java
  12. 59 0
      qmjszx-common/src/main/java/beilv/common/utils/ServiceException.java
  13. 106 0
      qmjszx-common/src/main/java/beilv/common/utils/rsa/RsaUtil.java
  14. 17 0
      qmjszx-common/src/main/java/beilv/common/utils/rsa/SecretKeyBo.java
  15. 55 0
      qmjszx-common/src/main/java/beilv/common/weixin/config/WxMiniProgramConfig.java
  16. 18 0
      qmjszx-common/src/main/java/beilv/common/weixin/config/WxMiniProgramProperties.java
  17. 61 80
      qmjszx-framework/src/main/java/beilv/framework/config/ShiroConfig.java
  18. 29 67
      qmjszx-framework/src/main/java/beilv/framework/shiro/realm/UserRealm.java
  19. 54 78
      qmjszx-framework/src/main/java/beilv/framework/shiro/service/SysLoginService.java
  20. 20 30
      qmjszx-framework/src/main/java/beilv/framework/shiro/service/SysPasswordService.java
  21. 17 39
      qmjszx-framework/src/main/java/beilv/framework/shiro/service/SysRegisterService.java
  22. 11 15
      qmjszx-framework/src/main/java/beilv/framework/shiro/service/SysShiroService.java
  23. 14 25
      qmjszx-framework/src/main/java/beilv/framework/shiro/web/CustomShiroFilterFactoryBean.java
  24. 54 0
      qmjszx-framework/src/main/java/beilv/framework/shiro/web/filter/wxMiniAppAccessControlFilter/WxMiniAppAccessControlFilter.java
  25. 27 0
      qmjszx-system/src/main/java/beilv/system/domain/vo/AppAuthLoginRespVO.java
  26. 22 0
      qmjszx-system/src/main/java/beilv/system/domain/vo/AppAuthWeixinMiniAppLoginReqVO.java
  27. 28 0
      qmjszx-system/src/main/java/beilv/system/service/IAppAuthService.java
  28. 11 0
      qmjszx-system/src/main/java/beilv/system/service/ISysMemberService.java
  29. 86 0
      qmjszx-system/src/main/java/beilv/system/service/impl/AppAuthServiceImpl.java
  30. 22 0
      qmjszx-system/src/main/java/beilv/system/service/impl/SysMemberServiceImpl.java

+ 5 - 0
qmjszx-admin/pom.xml

@@ -73,6 +73,11 @@
             <artifactId>qmjszx-business</artifactId>
         </dependency>
 
+        <!-- 支付模块 -->
+        <dependency>
+            <groupId>qmjszx</groupId>
+            <artifactId>qmjszx-pay</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 45 - 0
qmjszx-admin/src/main/java/beilv/web/controller/system/AppAuthController.java

@@ -0,0 +1,45 @@
+package beilv.web.controller.system;
+
+import beilv.common.core.domain.AjaxResult;
+import beilv.system.domain.SysMember;
+import beilv.system.domain.vo.AppAuthWeixinMiniAppLoginReqVO;
+import beilv.system.service.IAppAuthService;
+import beilv.system.service.ISysMemberService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+
+
+@Tag(name = "用户 APP - 认证")
+@RestController
+@RequestMapping("/app-api")
+@Validated
+@Slf4j
+public class AppAuthController {
+
+    @Resource
+    private IAppAuthService appAuthService;
+    @Resource
+    private ISysMemberService sysMemberService;
+
+
+    @PostMapping("/login")
+    @Operation(summary = "微信小程序的一键登录")
+    public AjaxResult weixinMiniAppLogin(@RequestBody @Valid AppAuthWeixinMiniAppLoginReqVO reqVO) {
+        return AjaxResult.success(appAuthService.weixinMiniAppLogin(reqVO));
+    }
+
+    @PostMapping("/testAuth")
+    @Operation(summary = "测试认证接口")
+    public AjaxResult testAuth(HttpServletRequest request) throws Exception {
+        SysMember loginSysMember = sysMemberService.getLoginSysMember(request);
+        return AjaxResult.success();
+    }
+
+}

+ 9 - 0
qmjszx-admin/src/main/resources/application.yml

@@ -140,3 +140,12 @@ xss:
 swagger:
   # 是否开启swagger
   enabled: true
+#RSA加密配置
+rsa:
+  # 公钥
+  publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIdmTeQdUyYK2a5ivPXdYArOgDMDKu6ob7YPlaC6R1/XxjHhGaSIucYGrzO+JhCFNkhgHxmc4POpyFq+PFdLuNMCAwEAAQ==
+  # 私钥
+  privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAh2ZN5B1TJgrZrmK89d1gCs6AMwMq7qhvtg+VoLpHX9fGMeEZpIi5xgavM74mEIU2SGAfGZzg86nIWr48V0u40wIDAQABAkBbgNXu7ap9sSN/aJcPCXaYlwmob+GZvBcS0OFr57fImsxTEzWg/OJWxcbHk7Li31AbHjwqQmVtUxk2wQ6GawthAiEAve5+CCaUHV3BIH7LhzU7MsPAr6IIx25t1NkNNg+dn3kCIQC2f7XtdTOVRtPUwO7QhwC0fX/wJu53pR01p55tqmgLqwIgYzUHr8o243/tOMQCG4W6fjGxnAvO+hy8UcluFSbi9kECIQCAK4NOyPA4V6zwD6vpgdcJ69YNiJoUJz8zboxCwtodzwIgNUN6uuiVJuZmBJDm+9AIY7Ury+ajGRZJ7l1FIBbxMPY=
+
+
+

+ 25 - 0
qmjszx-common/pom.xml

@@ -100,6 +100,31 @@
             <artifactId>lombok</artifactId>
             <version>1.18.26</version>
         </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.15</version>
+        </dependency>
+
+        <!-- 微信小程序 -->
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-miniapp</artifactId>
+            <version>4.4.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>31.1-jre</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
+        </dependency>
     </dependencies>
 
 </project>

+ 19 - 0
qmjszx-common/src/main/java/beilv/common/enums/ErrorCodeConstants.java

@@ -0,0 +1,19 @@
+package beilv.common.enums;
+
+
+import beilv.common.utils.ErrorCode;
+
+/**
+ * Member 错误码枚举类
+ * <p>
+ * member 系统,使用 1-004-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+    ErrorCode AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1004003006, "获得手机号失败");
+
+    ErrorCode MINI_AUTH_LOGIN_BAD = new ErrorCode(1004004002, "登录失败,请联系管理员");
+
+
+
+}

+ 31 - 0
qmjszx-common/src/main/java/beilv/common/enums/LoginTypeEnum.java

@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2018-2022
+ * All rights reserved, Designed By www.yixiang.co
+ */
+package beilv.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author hupeng
+ * 账单明细相关枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum LoginTypeEnum {
+
+
+    WXAPP("wxapp", "微信小程序"),
+    ALIAPP("aliapp", "支付宝小程序"),
+    WECHAT("wechat", "微信公众号"),
+    H5("h5", "h5"),
+    PC("pc", "pc"),
+    APP("app", "APP");
+
+
+    private String value;
+    private String desc;
+
+
+}

+ 144 - 0
qmjszx-common/src/main/java/beilv/common/json/JsonUtils.java

@@ -0,0 +1,144 @@
+package beilv.common.json;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.SneakyThrows;
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JSON 工具类
+ *
+ * @author yshop
+ */
+@UtilityClass
+@Slf4j
+public class JsonUtils {
+
+    private static ObjectMapper objectMapper = new ObjectMapper();
+
+    static {
+        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
+    }
+
+    /**
+     * 初始化 objectMapper 属性
+     * <p>
+     * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
+     *
+     * @param objectMapper ObjectMapper 对象
+     */
+    public static void init(ObjectMapper objectMapper) {
+        JsonUtils.objectMapper = objectMapper;
+    }
+
+    @SneakyThrows
+    public static String toJsonString(Object object) {
+        return objectMapper.writeValueAsString(object);
+    }
+
+    @SneakyThrows
+    public static byte[] toJsonByte(Object object) {
+        return objectMapper.writeValueAsBytes(object);
+    }
+
+    @SneakyThrows
+    public static String toJsonPrettyString(Object object) {
+        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
+    }
+
+    public static <T> T parseObject(String text, Class<T> clazz) {
+        if (StrUtil.isEmpty(text)) {
+            return null;
+        }
+        try {
+            return objectMapper.readValue(text, clazz);
+        } catch (IOException e) {
+            log.error("json parse err,json:{}", text, e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 将字符串解析成指定类型的对象
+     * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
+     * 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。
+     *
+     * @param text  字符串
+     * @param clazz 类型
+     * @return 对象
+     */
+    public static <T> T parseObject2(String text, Class<T> clazz) {
+        if (StrUtil.isEmpty(text)) {
+            return null;
+        }
+        return JSONUtil.toBean(text, clazz);
+    }
+
+    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
+        if (ArrayUtil.isEmpty(bytes)) {
+            return null;
+        }
+        try {
+            return objectMapper.readValue(bytes, clazz);
+        } catch (IOException e) {
+            log.error("json parse err,json:{}", bytes, e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
+        try {
+            return objectMapper.readValue(text, typeReference);
+        } catch (IOException e) {
+            log.error("json parse err,json:{}", text, e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> List<T> parseArray(String text, Class<T> clazz) {
+        if (StrUtil.isEmpty(text)) {
+            return new ArrayList<>();
+        }
+        try {
+            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
+        } catch (IOException e) {
+            log.error("json parse err,json:{}", text, e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static JsonNode parseTree(String text) {
+        try {
+            return objectMapper.readTree(text);
+        } catch (IOException e) {
+            log.error("json parse err,json:{}", text, e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static JsonNode parseTree(byte[] text) {
+        try {
+            return objectMapper.readTree(text);
+        } catch (IOException e) {
+            log.error("json parse err,json:{}", text, e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static boolean isJson(String text) {
+        return JSONUtil.isTypeJSON(text);
+    }
+
+}

+ 27 - 0
qmjszx-common/src/main/java/beilv/common/logger/LoginLogTypeEnum.java

@@ -0,0 +1,27 @@
+package beilv.common.logger;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 登录日志的类型枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum LoginLogTypeEnum {
+
+    LOGIN_USERNAME(100), // 使用账号登录
+    LOGIN_SOCIAL(101), // 使用社交登录
+    LOGIN_MOBILE(103), // 使用手机登陆
+    LOGIN_SMS(104), // 使用短信登陆
+
+    LOGOUT_SELF(200),  // 自己主动登出
+    LOGOUT_DELETE(202), // 强制退出
+    ;
+
+    /**
+     * 日志类型
+     */
+    private final Integer type;
+
+}

+ 110 - 0
qmjszx-common/src/main/java/beilv/common/servlet/ServletUtils.java

@@ -0,0 +1,110 @@
+package beilv.common.servlet;
+
+import beilv.common.json.JsonUtils;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import org.springframework.http.MediaType;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.Map;
+
+/**
+ * 客户端工具类
+ *
+ * @author yshop
+ */
+public class ServletUtils {
+
+    /**
+     * 返回 JSON 字符串
+     *
+     * @param response 响应
+     * @param object   对象,会序列化成 JSON 字符串
+     */
+    @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
+    public static void writeJSON(HttpServletResponse response, Object object) {
+        String content = JsonUtils.toJsonString(object);
+        ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
+    }
+
+    /**
+     * 返回附件
+     *
+     * @param response 响应
+     * @param filename 文件名
+     * @param content  附件内容
+     */
+    public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
+        // 设置 header 和 contentType
+        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
+        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+        // 输出附件
+        IoUtil.write(response.getOutputStream(), false, content);
+    }
+
+    /**
+     * @param request 请求
+     * @return ua
+     */
+    public static String getUserAgent(HttpServletRequest request) {
+        String ua = request.getHeader("User-Agent");
+        return ua != null ? ua : "";
+    }
+
+    /**
+     * 获得请求
+     *
+     * @return HttpServletRequest
+     */
+    public static HttpServletRequest getRequest() {
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        if (!(requestAttributes instanceof ServletRequestAttributes)) {
+            return null;
+        }
+        return ((ServletRequestAttributes) requestAttributes).getRequest();
+    }
+
+    public static String getUserAgent() {
+        HttpServletRequest request = getRequest();
+        if (request == null) {
+            return null;
+        }
+        return getUserAgent(request);
+    }
+
+    public static String getClientIP() {
+        HttpServletRequest request = getRequest();
+        if (request == null) {
+            return null;
+        }
+        return ServletUtil.getClientIP(request);
+    }
+
+    public static boolean isJsonRequest(ServletRequest request) {
+        return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
+    }
+
+    public static String getBody(HttpServletRequest request) {
+        return ServletUtil.getBody(request);
+    }
+
+    public static byte[] getBodyBytes(HttpServletRequest request) {
+        return ServletUtil.getBodyBytes(request);
+    }
+
+    public static String getClientIP(HttpServletRequest request) {
+        return ServletUtil.getClientIP(request);
+    }
+
+    public static Map<String, String> getParamMap(HttpServletRequest request) {
+        return ServletUtil.getParamMap(request);
+    }
+}

+ 28 - 0
qmjszx-common/src/main/java/beilv/common/utils/ErrorCode.java

@@ -0,0 +1,28 @@
+package beilv.common.utils;
+
+import lombok.Data;
+
+/**
+ * 错误码对象
+ * <p>
+ * <p>
+ * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
+ */
+@Data
+public class ErrorCode {
+
+    /**
+     * 错误码
+     */
+    private final Integer code;
+    /**
+     * 错误提示
+     */
+    private final String msg;
+
+    public ErrorCode(Integer code, String message) {
+        this.code = code;
+        this.msg = message;
+    }
+
+}

+ 70 - 24
qmjszx-common/src/main/java/beilv/common/utils/ExceptionUtil.java

@@ -1,37 +1,38 @@
 package beilv.common.utils;
 
+import com.google.common.annotations.VisibleForTesting;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import org.apache.commons.lang3.exception.ExceptionUtils;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 /**
  * 错误信息处理类。
  *
  * @author ruoyi
  */
-public class ExceptionUtil
-{
+@Slf4j
+public class ExceptionUtil {
     /**
      * 获取exception的详细错误信息。
      */
-    public static String getExceptionMessage(Throwable e)
-    {
+    public static String getExceptionMessage(Throwable e) {
         StringWriter sw = new StringWriter();
         e.printStackTrace(new PrintWriter(sw, true));
         return sw.toString();
     }
 
-    public static String getRootErrorMessage(Exception e)
-    {
+    public static String getRootErrorMessage(Exception e) {
         Throwable root = ExceptionUtils.getRootCause(e);
         root = (root == null ? e : root);
-        if (root == null)
-        {
+        if (root == null) {
             return "";
         }
         String msg = root.getMessage();
-        if (msg == null)
-        {
+        if (msg == null) {
             return "null";
         }
         return StringUtils.defaultString(msg);
@@ -39,24 +40,18 @@ public class ExceptionUtil
 
     /**
      * 检测异常e被触发的原因是不是因为异常cause。
-     * 
-     * @param e 捕获的异常。
+     *
+     * @param e     捕获的异常。
      * @param cause 异常触发原因。
      * @return 如果异常e是由cause类异常触发,则返回true;否则返回false。
      */
-    public static boolean isCausedBy(final Throwable e, final Class<? extends Throwable> cause)
-    {
-        if (cause.isAssignableFrom(e.getClass()))
-        {
+    public static boolean isCausedBy(final Throwable e, final Class<? extends Throwable> cause) {
+        if (cause.isAssignableFrom(e.getClass())) {
             return true;
-        }
-        else
-        {
+        } else {
             Throwable t = e.getCause();
-            while (t != null && t != e)
-            {
-                if (cause.isAssignableFrom(t.getClass()))
-                {
+            while (t != null && t != e) {
+                if (cause.isAssignableFrom(t.getClass())) {
                     return true;
                 }
                 t = t.getCause();
@@ -64,4 +59,55 @@ public class ExceptionUtil
             return false;
         }
     }
+    /**
+     * 错误码提示模板
+     */
+    private static final ConcurrentMap<Integer, String> MESSAGES = new ConcurrentHashMap<>();
+
+    public static ServiceException exception(ErrorCode errorCode) {
+        String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg());
+        return exception0(errorCode.getCode(), messagePattern);
+    }
+
+    public static ServiceException exception0(Integer code, String messagePattern, Object... params) {
+        String message = doFormat(code, messagePattern, params);
+        return new ServiceException(code, message);
+    }
+
+    /**
+     * 将错误编号对应的消息使用 params 进行格式化。
+     *
+     * @param code           错误编号
+     * @param messagePattern 消息模版
+     * @param params         参数
+     * @return 格式化后的提示
+     */
+    @VisibleForTesting
+    public static String doFormat(int code, String messagePattern, Object... params) {
+        StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
+        int i = 0;
+        int j;
+        int l;
+        for (l = 0; l < params.length; l++) {
+            j = messagePattern.indexOf("{}", i);
+            if (j == -1) {
+                log.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
+                if (i == 0) {
+                    return messagePattern;
+                } else {
+                    sbuf.append(messagePattern.substring(i));
+                    return sbuf.toString();
+                }
+            } else {
+                sbuf.append(messagePattern, i, j);
+                sbuf.append(params[l]);
+                i = j + 2;
+            }
+        }
+        if (messagePattern.indexOf("{}", i) != -1) {
+            log.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
+        }
+        sbuf.append(messagePattern.substring(i));
+        return sbuf.toString();
+    }
 }

+ 59 - 0
qmjszx-common/src/main/java/beilv/common/utils/ServiceException.java

@@ -0,0 +1,59 @@
+package beilv.common.utils;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 业务逻辑异常 Exception
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public final class ServiceException extends RuntimeException {
+
+    /**
+     * 业务错误码
+     *
+     * @see
+     */
+    private Integer code;
+    /**
+     * 错误提示
+     */
+    private String message;
+
+    /**
+     * 空构造方法,避免反序列化问题
+     */
+    public ServiceException() {
+    }
+
+    public ServiceException(ErrorCode errorCode) {
+        this.code = errorCode.getCode();
+        this.message = errorCode.getMsg();
+    }
+
+    public ServiceException(Integer code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public ServiceException setCode(Integer code) {
+        this.code = code;
+        return this;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    public ServiceException setMessage(String message) {
+        this.message = message;
+        return this;
+    }
+
+}

+ 106 - 0
qmjszx-common/src/main/java/beilv/common/utils/rsa/RsaUtil.java

@@ -0,0 +1,106 @@
+package beilv.common.utils.rsa;
+
+import javax.crypto.Cipher;
+import java.nio.charset.StandardCharsets;
+import java.security.*;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+/**
+ * 1.后端使用RSA算法生成一套密钥
+ * 2.前端使用接口获取后端公钥
+ * 3.后端将公钥传给前端,私钥留在本地服务器
+ * 4.前端使用公钥对明文加密,传输到后端
+ * 5.后端使用私钥解密,将密文转为明文
+ * Author 李猛
+ */
+public class RsaUtil {
+
+    /**
+     * 密钥长度 于原文长度对应 以及越长速度越慢
+     */
+    private final static int KEY_SIZE = 512;
+
+    /**
+     * 随机生成的一套密钥对
+     */
+    public static SecretKeyBo genKeyPair() throws NoSuchAlgorithmException {
+        // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
+        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
+        // 初始化密钥对生成器
+        keyPairGen.initialize(KEY_SIZE, new SecureRandom());
+        // 生成一个密钥对,保存在keyPair中
+        KeyPair keyPair = keyPairGen.generateKeyPair();
+        // 得到私钥
+        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+        // 得到公钥
+        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+        String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
+        // 得到私钥字符串
+        String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
+
+        return new SecretKeyBo()
+                .setPrivateKey(privateKeyString)
+                .setPublicKey(publicKeyString);
+    }
+
+    /**
+     * RSA公钥加密
+     *
+     * @param str    加密字符串
+     * @param publicKey 公钥
+     * @return 密文
+     * @throws Exception 加密过程中的异常信息
+     */
+    public static String encrypt(String str, String publicKey) throws Exception {
+        //base64编码的公钥
+        byte[] decoded = Base64.getDecoder().decode(publicKey);
+        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
+        //RSA加密
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
+        return Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
+    }
+
+    /**
+     * RSA私钥解密
+     *
+     * @param str    加密字符串
+     * @param privateKey 私钥
+     * @return 明文字符串
+     * @throws Exception 解密过程中的异常信息
+     */
+    public static String decrypt(String str, String privateKey) throws Exception {
+        //64位解码加密后的字符串
+        byte[] inputByte = Base64.getDecoder().decode(str);
+        //base64编码的私钥
+        byte[] decoded = Base64.getDecoder().decode(privateKey);
+        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
+        //RSA解密
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.DECRYPT_MODE, priKey);
+        return new String(cipher.doFinal(inputByte));
+    }
+
+    public static void main(String[] args) throws Exception {
+        SecretKeyBo bo = RsaUtil.genKeyPair();
+        //公钥
+        System.out.println(bo.getPublicKey());
+        //私钥
+        System.out.println(bo.getPrivateKey());
+        //加密
+        String str1 = RsaUtil.encrypt("25",bo.getPublicKey());
+        System.out.println(str1);
+        //解密
+        String str2 = RsaUtil.decrypt(str1,bo.getPrivateKey());
+        System.out.println(str2);
+    }
+
+}
+
+
+
+

+ 17 - 0
qmjszx-common/src/main/java/beilv/common/utils/rsa/SecretKeyBo.java

@@ -0,0 +1,17 @@
+package beilv.common.utils.rsa;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * RSA随机秘钥实体类
+ * Author 李猛
+ * */
+@Data
+@Accessors(chain = true)
+public class SecretKeyBo {
+    private String publicKey;
+    private String privateKey;
+}
+
+

+ 55 - 0
qmjszx-common/src/main/java/beilv/common/weixin/config/WxMiniProgramConfig.java

@@ -0,0 +1,55 @@
+package beilv.common.weixin.config;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 微信小程序配置文件
+ *
+ * @author: moxiangrong
+ **/
+@Configuration
+@ConditionalOnClass(WxMaService.class)
+@EnableConfigurationProperties(WxMiniProgramProperties.class)
+public class WxMiniProgramConfig {
+
+    /*注入小程序相关配置*/
+    @Autowired
+    private WxMiniProgramProperties properties;
+
+    /**
+     * 配置默认参数
+     */
+    @Bean
+    @ConditionalOnMissingBean
+    public WxMaConfig wxMaConfig() {
+        WxMaDefaultConfigImpl wxMaDefaultConfig = new WxMaDefaultConfigImpl();
+        //设置默认参数-appid,secret
+//        wxMaDefaultConfig.setAppid(this.properties.getAppid());
+//        wxMaDefaultConfig.setSecret(this.properties.getSecret());
+        wxMaDefaultConfig.setAppid("wxe44c64c20fa20d69");
+        wxMaDefaultConfig.setSecret("bbaaa2292da0bcc3336dc2a0f2f8493a");
+        return wxMaDefaultConfig;
+    }
+
+    /**
+     * 配置WxMaService
+     */
+    @Bean
+    @ConditionalOnMissingBean
+    public WxMaService wxMaService(WxMaConfig wxMaConfig) {
+        WxMaService wxMaService = new WxMaServiceImpl();
+        wxMaService.setWxMaConfig(wxMaConfig);
+        return wxMaService;
+    }
+
+
+}

+ 18 - 0
qmjszx-common/src/main/java/beilv/common/weixin/config/WxMiniProgramProperties.java

@@ -0,0 +1,18 @@
+package beilv.common.weixin.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+@ConfigurationProperties(prefix = "wx.miniapp")
+@Validated
+@Data
+public class WxMiniProgramProperties {
+
+    /*微信小程序app_id*/
+    private String appid;
+
+    /*微信小程序app_secret*/
+    private String secret;
+
+}

+ 61 - 80
qmjszx-framework/src/main/java/beilv/framework/config/ShiroConfig.java

@@ -1,26 +1,6 @@
 package beilv.framework.config;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import javax.servlet.Filter;
-import org.apache.commons.io.IOUtils;
-import org.apache.shiro.cache.ehcache.EhCacheManager;
-import org.apache.shiro.codec.Base64;
-import org.apache.shiro.config.ConfigurationException;
-import org.apache.shiro.io.ResourceUtils;
-import org.apache.shiro.mgt.SecurityManager;
-import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
-import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
-import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
-import org.apache.shiro.web.servlet.SimpleCookie;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
+import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
 import beilv.common.constant.Constants;
 import beilv.common.utils.StringUtils;
 import beilv.common.utils.security.CipherUtils;
@@ -32,22 +12,46 @@ import beilv.framework.shiro.session.OnlineSessionDAO;
 import beilv.framework.shiro.session.OnlineSessionFactory;
 import beilv.framework.shiro.web.CustomShiroFilterFactoryBean;
 import beilv.framework.shiro.web.filter.LogoutFilter;
+import beilv.framework.shiro.web.filter.wxMiniAppAccessControlFilter.WxMiniAppAccessControlFilter;
 import beilv.framework.shiro.web.filter.captcha.CaptchaValidateFilter;
 import beilv.framework.shiro.web.filter.kickout.KickoutSessionFilter;
 import beilv.framework.shiro.web.filter.online.OnlineSessionFilter;
 import beilv.framework.shiro.web.filter.sync.SyncOnlineSessionFilter;
 import beilv.framework.shiro.web.session.OnlineWebSessionManager;
 import beilv.framework.shiro.web.session.SpringSessionValidationScheduler;
-import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
+import org.apache.commons.io.IOUtils;
+import org.apache.shiro.cache.ehcache.EhCacheManager;
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.config.ConfigurationException;
+import org.apache.shiro.io.ResourceUtils;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
+import org.apache.shiro.web.filter.mgt.FilterChainManager;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.servlet.SimpleCookie;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.servlet.Filter;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * 权限配置加载
- * 
+ *
  * @author ruoyi
  */
 @Configuration
-public class ShiroConfig
-{
+public class ShiroConfig {
     /**
      * Session超时时间,单位为毫秒(默认30分钟)
      */
@@ -136,17 +140,13 @@ public class ShiroConfig
      * 缓存管理器 使用Ehcache实现
      */
     @Bean
-    public EhCacheManager getEhCacheManager()
-    {
+    public EhCacheManager getEhCacheManager() {
         net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi");
         EhCacheManager em = new EhCacheManager();
-        if (StringUtils.isNull(cacheManager))
-        {
+        if (StringUtils.isNull(cacheManager)) {
             em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()));
             return em;
-        }
-        else
-        {
+        } else {
             em.setCacheManager(cacheManager);
             return em;
         }
@@ -155,24 +155,18 @@ public class ShiroConfig
     /**
      * 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署
      */
-    protected InputStream getCacheManagerConfigFileInputStream()
-    {
+    protected InputStream getCacheManagerConfigFileInputStream() {
         String configFile = "classpath:ehcache/ehcache-shiro.xml";
         InputStream inputStream = null;
-        try
-        {
+        try {
             inputStream = ResourceUtils.getInputStreamForPath(configFile);
             byte[] b = IOUtils.toByteArray(inputStream);
             InputStream in = new ByteArrayInputStream(b);
             return in;
-        }
-        catch (IOException e)
-        {
+        } catch (IOException e) {
             throw new ConfigurationException(
                     "Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e);
-        }
-        finally
-        {
+        } finally {
             IOUtils.closeQuietly(inputStream);
         }
     }
@@ -181,8 +175,7 @@ public class ShiroConfig
      * 自定义Realm
      */
     @Bean
-    public UserRealm userRealm(EhCacheManager cacheManager)
-    {
+    public UserRealm userRealm(EhCacheManager cacheManager) {
         UserRealm userRealm = new UserRealm();
         userRealm.setAuthorizationCacheName(Constants.SYS_AUTH_CACHE);
         userRealm.setCacheManager(cacheManager);
@@ -193,8 +186,7 @@ public class ShiroConfig
      * 自定义sessionDAO会话
      */
     @Bean
-    public OnlineSessionDAO sessionDAO()
-    {
+    public OnlineSessionDAO sessionDAO() {
         OnlineSessionDAO sessionDAO = new OnlineSessionDAO();
         return sessionDAO;
     }
@@ -203,8 +195,7 @@ public class ShiroConfig
      * 自定义sessionFactory会话
      */
     @Bean
-    public OnlineSessionFactory sessionFactory()
-    {
+    public OnlineSessionFactory sessionFactory() {
         OnlineSessionFactory sessionFactory = new OnlineSessionFactory();
         return sessionFactory;
     }
@@ -213,8 +204,7 @@ public class ShiroConfig
      * 会话管理器
      */
     @Bean
-    public OnlineWebSessionManager sessionManager()
-    {
+    public OnlineWebSessionManager sessionManager() {
         OnlineWebSessionManager manager = new OnlineWebSessionManager();
         // 加入缓存管理器
         manager.setCacheManager(getEhCacheManager());
@@ -239,8 +229,7 @@ public class ShiroConfig
      * 安全管理器
      */
     @Bean
-    public SecurityManager securityManager(UserRealm userRealm)
-    {
+    public SecurityManager securityManager(UserRealm userRealm) {
         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
         // 设置realm.
         securityManager.setRealm(userRealm);
@@ -256,19 +245,18 @@ public class ShiroConfig
     /**
      * 退出过滤器
      */
-    public LogoutFilter logoutFilter()
-    {
+    public LogoutFilter logoutFilter() {
         LogoutFilter logoutFilter = new LogoutFilter();
         logoutFilter.setLoginUrl(loginUrl);
         return logoutFilter;
     }
-
+    @Autowired
+    private WxMiniAppAccessControlFilter wxMiniAppAccessControlFilter;
     /**
      * Shiro过滤器配置
      */
     @Bean
-    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
-    {
+    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
         CustomShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();
         // Shiro的核心安全接口,这个属性是必须的
         shiroFilterFactoryBean.setSecurityManager(securityManager);
@@ -292,14 +280,15 @@ public class ShiroConfig
         filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
         // 匿名访问不鉴权注解列表
         List<String> permitAllUrl = SpringUtils.getBean(PermitAllUrlProperties.class).getUrls();
-        if (StringUtils.isNotEmpty(permitAllUrl))
-        {
+        if (StringUtils.isNotEmpty(permitAllUrl)) {
             permitAllUrl.forEach(url -> filterChainDefinitionMap.put(url, "anon"));
         }
         // 退出 logout地址,shiro去清除session
         filterChainDefinitionMap.put("/logout", "logout");
         // 不需要拦截的访问
         filterChainDefinitionMap.put("/login", "anon,captchaValidate");
+        // 微信小程序登录
+//        filterChainDefinitionMap.put("/app-api/**", "anon,captchaValidate");
         // 注册相关
         filterChainDefinitionMap.put("/register", "anon,captchaValidate");
         // 系统权限列表
@@ -310,11 +299,13 @@ public class ShiroConfig
         filters.put("syncOnlineSession", syncOnlineSessionFilter());
         filters.put("captchaValidate", captchaValidateFilter());
         filters.put("kickout", kickoutSessionFilter());
+        filters.put("wxAccessControl", wxMiniAppAccessControlFilter); // 添加自定义过滤器
         // 注销成功,则跳转到指定页面
         filters.put("logout", logoutFilter());
         shiroFilterFactoryBean.setFilters(filters);
 
         // 所有请求需要认证
+        filterChainDefinitionMap.put("/app-api/**", "wxAccessControl");
         filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession");
         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
 
@@ -324,8 +315,7 @@ public class ShiroConfig
     /**
      * 自定义在线用户处理过滤器
      */
-    public OnlineSessionFilter onlineSessionFilter()
-    {
+    public OnlineSessionFilter onlineSessionFilter() {
         OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter();
         onlineSessionFilter.setLoginUrl(loginUrl);
         onlineSessionFilter.setOnlineSessionDAO(sessionDAO());
@@ -335,8 +325,7 @@ public class ShiroConfig
     /**
      * 自定义在线用户同步过滤器
      */
-    public SyncOnlineSessionFilter syncOnlineSessionFilter()
-    {
+    public SyncOnlineSessionFilter syncOnlineSessionFilter() {
         SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter();
         syncOnlineSessionFilter.setOnlineSessionDAO(sessionDAO());
         return syncOnlineSessionFilter;
@@ -345,8 +334,7 @@ public class ShiroConfig
     /**
      * 自定义验证码过滤器
      */
-    public CaptchaValidateFilter captchaValidateFilter()
-    {
+    public CaptchaValidateFilter captchaValidateFilter() {
         CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();
         captchaValidateFilter.setCaptchaEnabled(captchaEnabled);
         captchaValidateFilter.setCaptchaType(captchaType);
@@ -356,8 +344,7 @@ public class ShiroConfig
     /**
      * cookie 属性设置
      */
-    public SimpleCookie rememberMeCookie()
-    {
+    public SimpleCookie rememberMeCookie() {
         SimpleCookie cookie = new SimpleCookie("rememberMe");
         cookie.setDomain(domain);
         cookie.setPath(path);
@@ -369,16 +356,12 @@ public class ShiroConfig
     /**
      * 记住我
      */
-    public CustomCookieRememberMeManager rememberMeManager()
-    {
+    public CustomCookieRememberMeManager rememberMeManager() {
         CustomCookieRememberMeManager cookieRememberMeManager = new CustomCookieRememberMeManager();
         cookieRememberMeManager.setCookie(rememberMeCookie());
-        if (StringUtils.isNotEmpty(cipherKey))
-        {
+        if (StringUtils.isNotEmpty(cipherKey)) {
             cookieRememberMeManager.setCipherKey(Base64.decode(cipherKey));
-        }
-        else
-        {
+        } else {
             cookieRememberMeManager.setCipherKey(CipherUtils.generateNewKey(128, "AES").getEncoded());
         }
         return cookieRememberMeManager;
@@ -387,8 +370,7 @@ public class ShiroConfig
     /**
      * 同一个用户多设备登录限制
      */
-    public KickoutSessionFilter kickoutSessionFilter()
-    {
+    public KickoutSessionFilter kickoutSessionFilter() {
         KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter();
         kickoutSessionFilter.setCacheManager(getEhCacheManager());
         kickoutSessionFilter.setSessionManager(sessionManager());
@@ -405,8 +387,7 @@ public class ShiroConfig
      * thymeleaf模板引擎和shiro框架的整合
      */
     @Bean
-    public ShiroDialect shiroDialect()
-    {
+    public ShiroDialect shiroDialect() {
         return new ShiroDialect();
     }
 
@@ -415,10 +396,10 @@ public class ShiroConfig
      */
     @Bean
     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
-            @Qualifier("securityManager") SecurityManager securityManager)
-    {
+            @Qualifier("securityManager") SecurityManager securityManager) {
         AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
         authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
         return authorizationAttributeSourceAdvisor;
     }
+
 }

+ 29 - 67
qmjszx-framework/src/main/java/beilv/framework/shiro/realm/UserRealm.java

@@ -1,16 +1,12 @@
 package beilv.framework.shiro.realm;
 
-import java.util.HashSet;
-import java.util.Set;
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.ExcessiveAttemptsException;
-import org.apache.shiro.authc.IncorrectCredentialsException;
-import org.apache.shiro.authc.LockedAccountException;
-import org.apache.shiro.authc.SimpleAuthenticationInfo;
-import org.apache.shiro.authc.UnknownAccountException;
-import org.apache.shiro.authc.UsernamePasswordToken;
+import beilv.common.core.domain.entity.SysUser;
+import beilv.common.exception.user.*;
+import beilv.common.utils.ShiroUtils;
+import beilv.framework.shiro.service.SysLoginService;
+import beilv.system.service.ISysMenuService;
+import beilv.system.service.ISysRoleService;
+import org.apache.shiro.authc.*;
 import org.apache.shiro.authz.AuthorizationInfo;
 import org.apache.shiro.authz.SimpleAuthorizationInfo;
 import org.apache.shiro.cache.Cache;
@@ -20,25 +16,16 @@ import org.apache.shiro.subject.SimplePrincipalCollection;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import beilv.common.core.domain.entity.SysUser;
-import beilv.common.exception.user.CaptchaException;
-import beilv.common.exception.user.RoleBlockedException;
-import beilv.common.exception.user.UserBlockedException;
-import beilv.common.exception.user.UserNotExistsException;
-import beilv.common.exception.user.UserPasswordNotMatchException;
-import beilv.common.exception.user.UserPasswordRetryLimitExceedException;
-import beilv.common.utils.ShiroUtils;
-import beilv.framework.shiro.service.SysLoginService;
-import beilv.system.service.ISysMenuService;
-import beilv.system.service.ISysRoleService;
+
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * 自定义Realm 处理登录 权限
- * 
+ *
  * @author ruoyi
  */
-public class UserRealm extends AuthorizingRealm
-{
+public class UserRealm extends AuthorizingRealm {
     private static final Logger log = LoggerFactory.getLogger(UserRealm.class);
 
     @Autowired
@@ -54,8 +41,7 @@ public class UserRealm extends AuthorizingRealm
      * 授权
      */
     @Override
-    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)
-    {
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
         SysUser user = ShiroUtils.getSysUser();
         // 角色列表
         Set<String> roles = new HashSet<String>();
@@ -63,13 +49,10 @@ public class UserRealm extends AuthorizingRealm
         Set<String> menus = new HashSet<String>();
         SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
         // 管理员拥有所有权限
-        if (user.isAdmin())
-        {
+        if (user.isAdmin()) {
             info.addRole("admin");
             info.addStringPermission("*:*:*");
-        }
-        else
-        {
+        } else {
             roles = roleService.selectRoleKeys(user.getUserId());
             menus = menuService.selectPermsByUserId(user.getUserId());
             // 角色加入AuthorizationInfo认证对象
@@ -84,47 +67,30 @@ public class UserRealm extends AuthorizingRealm
      * 登录认证
      */
     @Override
-    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
-    {
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
         UsernamePasswordToken upToken = (UsernamePasswordToken) token;
         String username = upToken.getUsername();
         String password = "";
-        if (upToken.getPassword() != null)
-        {
+        if (upToken.getPassword() != null) {
             password = new String(upToken.getPassword());
         }
 
         SysUser user = null;
-        try
-        {
+        try {
             user = loginService.login(username, password);
-        }
-        catch (CaptchaException e)
-        {
+        } catch (CaptchaException e) {
             throw new AuthenticationException(e.getMessage(), e);
-        }
-        catch (UserNotExistsException e)
-        {
+        } catch (UserNotExistsException e) {
             throw new UnknownAccountException(e.getMessage(), e);
-        }
-        catch (UserPasswordNotMatchException e)
-        {
+        } catch (UserPasswordNotMatchException e) {
             throw new IncorrectCredentialsException(e.getMessage(), e);
-        }
-        catch (UserPasswordRetryLimitExceedException e)
-        {
+        } catch (UserPasswordRetryLimitExceedException e) {
             throw new ExcessiveAttemptsException(e.getMessage(), e);
-        }
-        catch (UserBlockedException e)
-        {
+        } catch (UserBlockedException e) {
             throw new LockedAccountException(e.getMessage(), e);
-        }
-        catch (RoleBlockedException e)
-        {
+        } catch (RoleBlockedException e) {
             throw new LockedAccountException(e.getMessage(), e);
-        }
-        catch (Exception e)
-        {
+        } catch (Exception e) {
             log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
             throw new AuthenticationException(e.getMessage(), e);
         }
@@ -135,8 +101,7 @@ public class UserRealm extends AuthorizingRealm
     /**
      * 清理指定用户授权信息缓存
      */
-    public void clearCachedAuthorizationInfo(Object principal)
-    {
+    public void clearCachedAuthorizationInfo(Object principal) {
         SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
         this.clearCachedAuthorizationInfo(principals);
     }
@@ -144,13 +109,10 @@ public class UserRealm extends AuthorizingRealm
     /**
      * 清理所有用户授权信息缓存
      */
-    public void clearAllCachedAuthorizationInfo()
-    {
+    public void clearAllCachedAuthorizationInfo() {
         Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
-        if (cache != null)
-        {
-            for (Object key : cache.keys())
-            {
+        if (cache != null) {
+            for (Object key : cache.keys()) {
                 cache.remove(key);
             }
         }

+ 54 - 78
qmjszx-framework/src/main/java/beilv/framework/shiro/service/SysLoginService.java

@@ -1,41 +1,31 @@
 package beilv.framework.shiro.service;
 
-import java.util.List;
-import java.util.Set;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
 import beilv.common.constant.Constants;
 import beilv.common.constant.ShiroConstants;
 import beilv.common.constant.UserConstants;
 import beilv.common.core.domain.entity.SysRole;
 import beilv.common.core.domain.entity.SysUser;
 import beilv.common.enums.UserStatus;
-import beilv.common.exception.user.BlackListException;
-import beilv.common.exception.user.CaptchaException;
-import beilv.common.exception.user.UserBlockedException;
-import beilv.common.exception.user.UserDeleteException;
-import beilv.common.exception.user.UserNotExistsException;
-import beilv.common.exception.user.UserPasswordNotMatchException;
-import beilv.common.utils.DateUtils;
-import beilv.common.utils.IpUtils;
-import beilv.common.utils.MessageUtils;
-import beilv.common.utils.ServletUtils;
-import beilv.common.utils.ShiroUtils;
-import beilv.common.utils.StringUtils;
+import beilv.common.exception.user.*;
+import beilv.common.utils.*;
 import beilv.framework.manager.AsyncManager;
 import beilv.framework.manager.factory.AsyncFactory;
 import beilv.system.service.ISysConfigService;
 import beilv.system.service.ISysMenuService;
 import beilv.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Set;
 
 /**
  * 登录校验方法
- * 
+ *
  * @author ruoyi
  */
 @Component
-public class SysLoginService
-{
+public class SysLoginService {
     @Autowired
     private SysPasswordService passwordService;
 
@@ -51,40 +41,34 @@ public class SysLoginService
     /**
      * 登录
      */
-    public SysUser login(String username, String password)
-    {
+    public SysUser login(String username, String password) {
         // 验证码校验
-        if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA)))
-        {
+        if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
             throw new CaptchaException();
         }
         // 用户名或密码为空 错误
-        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
-        {
+        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
             throw new UserNotExistsException();
         }
         // 密码如果不在指定范围内 错误
         if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
-                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
-        {
+                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
             throw new UserPasswordNotMatchException();
         }
 
         // 用户名不在指定范围内 错误
         if (username.length() < UserConstants.USERNAME_MIN_LENGTH
-                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
-        {
+                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
             throw new UserPasswordNotMatchException();
         }
 
         // IP黑名单校验
         String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
-        if (IpUtils.isMatchedIp(blackStr, ShiroUtils.getIp()))
-        {
+        if (IpUtils.isMatchedIp(blackStr, ShiroUtils.getIp())) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
             throw new BlackListException();
         }
@@ -93,31 +77,28 @@ public class SysLoginService
         SysUser user = userService.selectUserByLoginName(username);
 
         /**
-        if (user == null && maybeMobilePhoneNumber(username))
-        {
-            user = userService.selectUserByPhoneNumber(username);
-        }
-
-        if (user == null && maybeEmail(username))
-        {
-            user = userService.selectUserByEmail(username);
-        }
-        */
-
-        if (user == null)
-        {
+         if (user == null && maybeMobilePhoneNumber(username))
+         {
+         user = userService.selectUserByPhoneNumber(username);
+         }
+
+         if (user == null && maybeEmail(username))
+         {
+         user = userService.selectUserByEmail(username);
+         }
+         */
+
+        if (user == null) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));
             throw new UserNotExistsException();
         }
-        
-        if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
-        {
+
+        if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));
             throw new UserDeleteException();
         }
-        
-        if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
-        {
+
+        if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked")));
             throw new UserBlockedException();
         }
@@ -131,40 +112,36 @@ public class SysLoginService
     }
 
     /**
-    private boolean maybeEmail(String username)
-    {
-        if (!username.matches(UserConstants.EMAIL_PATTERN))
-        {
-            return false;
-        }
-        return true;
-    }
-
-    private boolean maybeMobilePhoneNumber(String username)
-    {
-        if (!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN))
-        {
-            return false;
-        }
-        return true;
-    }
-    */
+     private boolean maybeEmail(String username)
+     {
+     if (!username.matches(UserConstants.EMAIL_PATTERN))
+     {
+     return false;
+     }
+     return true;
+     }
+
+     private boolean maybeMobilePhoneNumber(String username)
+     {
+     if (!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN))
+     {
+     return false;
+     }
+     return true;
+     }
+     */
 
     /**
      * 设置角色权限
      *
      * @param user 用户信息
      */
-    public void setRolePermission(SysUser user)
-    {
+    public void setRolePermission(SysUser user) {
         List<SysRole> roles = user.getRoles();
-        if (!roles.isEmpty())
-        {
+        if (!roles.isEmpty()) {
             // 设置permissions属性,以便数据权限匹配权限
-            for (SysRole role : roles)
-            {
-                if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL))
-                {
+            for (SysRole role : roles) {
+                if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL)) {
                     Set<String> rolePerms = menuService.selectPermsByRoleId(role.getRoleId());
                     role.setPermissions(rolePerms);
                 }
@@ -177,8 +154,7 @@ public class SysLoginService
      *
      * @param userId 用户ID
      */
-    public void recordLoginInfo(Long userId)
-    {
+    public void recordLoginInfo(Long userId) {
         SysUser user = new SysUser();
         user.setUserId(userId);
         user.setLoginIp(ShiroUtils.getIp());

+ 20 - 30
qmjszx-framework/src/main/java/beilv/framework/shiro/service/SysPasswordService.java

@@ -1,13 +1,5 @@
 package beilv.framework.shiro.service;
 
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.annotation.PostConstruct;
-import org.apache.shiro.cache.Cache;
-import org.apache.shiro.cache.CacheManager;
-import org.apache.shiro.crypto.hash.Md5Hash;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
 import beilv.common.constant.Constants;
 import beilv.common.constant.ShiroConstants;
 import beilv.common.core.domain.entity.SysUser;
@@ -16,15 +8,23 @@ import beilv.common.exception.user.UserPasswordRetryLimitExceedException;
 import beilv.common.utils.MessageUtils;
 import beilv.framework.manager.AsyncManager;
 import beilv.framework.manager.factory.AsyncFactory;
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheManager;
+import org.apache.shiro.crypto.hash.Md5Hash;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * 登录密码方法
- * 
+ *
  * @author ruoyi
  */
 @Component
-public class SysPasswordService
-{
+public class SysPasswordService {
     @Autowired
     private CacheManager cacheManager;
 
@@ -34,52 +34,42 @@ public class SysPasswordService
     private String maxRetryCount;
 
     @PostConstruct
-    public void init()
-    {
+    public void init() {
         loginRecordCache = cacheManager.getCache(ShiroConstants.LOGIN_RECORD_CACHE);
     }
 
-    public void validate(SysUser user, String password)
-    {
+    public void validate(SysUser user, String password) {
         String loginName = user.getLoginName();
 
         AtomicInteger retryCount = loginRecordCache.get(loginName);
 
-        if (retryCount == null)
-        {
+        if (retryCount == null) {
             retryCount = new AtomicInteger(0);
             loginRecordCache.put(loginName, retryCount);
         }
-        if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue())
-        {
+        if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue()) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount)));
             throw new UserPasswordRetryLimitExceedException(Integer.valueOf(maxRetryCount).intValue());
         }
 
-        if (!matches(user, password))
-        {
+        if (!matches(user, password)) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount)));
             loginRecordCache.put(loginName, retryCount);
             throw new UserPasswordNotMatchException();
-        }
-        else
-        {
+        } else {
             clearLoginRecordCache(loginName);
         }
     }
 
-    public boolean matches(SysUser user, String newPassword)
-    {
+    public boolean matches(SysUser user, String newPassword) {
         return user.getPassword().equals(encryptPassword(user.getLoginName(), newPassword, user.getSalt()));
     }
 
-    public void clearLoginRecordCache(String loginName)
-    {
+    public void clearLoginRecordCache(String loginName) {
         loginRecordCache.remove(loginName);
     }
 
-    public String encryptPassword(String loginName, String password, String salt)
-    {
+    public String encryptPassword(String loginName, String password, String salt) {
         return new Md5Hash(loginName + password + salt).toHex();
     }
 }

+ 17 - 39
qmjszx-framework/src/main/java/beilv/framework/shiro/service/SysRegisterService.java

@@ -1,28 +1,23 @@
 package beilv.framework.shiro.service;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
 import beilv.common.constant.Constants;
 import beilv.common.constant.ShiroConstants;
 import beilv.common.constant.UserConstants;
 import beilv.common.core.domain.entity.SysUser;
-import beilv.common.utils.DateUtils;
-import beilv.common.utils.MessageUtils;
-import beilv.common.utils.ServletUtils;
-import beilv.common.utils.ShiroUtils;
-import beilv.common.utils.StringUtils;
+import beilv.common.utils.*;
 import beilv.framework.manager.AsyncManager;
 import beilv.framework.manager.factory.AsyncFactory;
 import beilv.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 
 /**
  * 注册校验方法
- * 
+ *
  * @author ruoyi
  */
 @Component
-public class SysRegisterService
-{
+public class SysRegisterService {
     @Autowired
     private ISysUserService userService;
 
@@ -32,49 +27,32 @@ public class SysRegisterService
     /**
      * 注册
      */
-    public String register(SysUser user)
-    {
+    public String register(SysUser user) {
         String msg = "", loginName = user.getLoginName(), password = user.getPassword();
 
-        if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA)))
-        {
+        if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))) {
             msg = "验证码错误";
-        }
-        else if (StringUtils.isEmpty(loginName))
-        {
+        } else if (StringUtils.isEmpty(loginName)) {
             msg = "用户名不能为空";
-        }
-        else if (StringUtils.isEmpty(password))
-        {
+        } else if (StringUtils.isEmpty(password)) {
             msg = "用户密码不能为空";
-        }
-        else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
-                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
-        {
+        } else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
+                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
             msg = "密码长度必须在5到20个字符之间";
-        }
-        else if (loginName.length() < UserConstants.USERNAME_MIN_LENGTH
-                || loginName.length() > UserConstants.USERNAME_MAX_LENGTH)
-        {
+        } else if (loginName.length() < UserConstants.USERNAME_MIN_LENGTH
+                || loginName.length() > UserConstants.USERNAME_MAX_LENGTH) {
             msg = "账户长度必须在2到20个字符之间";
-        }
-        else if (!userService.checkLoginNameUnique(user))
-        {
+        } else if (!userService.checkLoginNameUnique(user)) {
             msg = "保存用户'" + loginName + "'失败,注册账号已存在";
-        }
-        else
-        {
+        } else {
             user.setPwdUpdateDate(DateUtils.getNowDate());
             user.setUserName(loginName);
             user.setSalt(ShiroUtils.randomSalt());
             user.setPassword(passwordService.encryptPassword(loginName, password, user.getSalt()));
             boolean regFlag = userService.registerUser(user);
-            if (!regFlag)
-            {
+            if (!regFlag) {
                 msg = "注册失败,请联系系统管理人员";
-            }
-            else
-            {
+            } else {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.REGISTER, MessageUtils.message("user.register.success")));
             }
         }

+ 11 - 15
qmjszx-framework/src/main/java/beilv/framework/shiro/service/SysShiroService.java

@@ -1,22 +1,22 @@
 package beilv.framework.shiro.service;
 
-import java.io.Serializable;
-import org.apache.shiro.session.Session;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
 import beilv.common.utils.StringUtils;
 import beilv.framework.shiro.session.OnlineSession;
 import beilv.system.domain.SysUserOnline;
 import beilv.system.service.ISysUserOnlineService;
+import org.apache.shiro.session.Session;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.Serializable;
 
 /**
  * 会话db操作处理
- * 
+ *
  * @author ruoyi
  */
 @Component
-public class SysShiroService
-{
+public class SysShiroService {
     @Autowired
     private ISysUserOnlineService onlineService;
 
@@ -25,8 +25,7 @@ public class SysShiroService
      *
      * @param onlineSession 会话信息
      */
-    public void deleteSession(OnlineSession onlineSession)
-    {
+    public void deleteSession(OnlineSession onlineSession) {
         onlineService.deleteOnlineById(String.valueOf(onlineSession.getId()));
     }
 
@@ -36,17 +35,14 @@ public class SysShiroService
      * @param sessionId
      * @return
      */
-    public Session getSession(Serializable sessionId)
-    {
+    public Session getSession(Serializable sessionId) {
         SysUserOnline userOnline = onlineService.selectOnlineById(String.valueOf(sessionId));
         return StringUtils.isNull(userOnline) ? null : createSession(userOnline);
     }
 
-    public Session createSession(SysUserOnline userOnline)
-    {
+    public Session createSession(SysUserOnline userOnline) {
         OnlineSession onlineSession = new OnlineSession();
-        if (StringUtils.isNotNull(userOnline))
-        {
+        if (StringUtils.isNotNull(userOnline)) {
             onlineSession.setId(userOnline.getSessionId());
             onlineSession.setHost(userOnline.getIpaddr());
             onlineSession.setBrowser(userOnline.getBrowser());

+ 14 - 25
qmjszx-framework/src/main/java/beilv/framework/shiro/web/CustomShiroFilterFactoryBean.java

@@ -1,5 +1,6 @@
 package beilv.framework.shiro.web;
 
+import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
 import org.apache.shiro.web.filter.InvalidRequestFilter;
 import org.apache.shiro.web.filter.mgt.DefaultFilter;
@@ -8,37 +9,32 @@ import org.apache.shiro.web.filter.mgt.FilterChainResolver;
 import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
 import org.apache.shiro.web.mgt.WebSecurityManager;
 import org.apache.shiro.web.servlet.AbstractShiroFilter;
-import org.apache.shiro.mgt.SecurityManager;
 import org.springframework.beans.factory.BeanInitializationException;
+
 import javax.servlet.Filter;
 import java.util.Map;
 
 /**
  * 自定义ShiroFilterFactoryBean解决资源中文路径问题
- * 
+ *
  * @author ruoyi
  */
-public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean
-{
+public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean {
     @Override
-    public Class<MySpringShiroFilter> getObjectType()
-    {
+    public Class<MySpringShiroFilter> getObjectType() {
         return MySpringShiroFilter.class;
     }
 
     @Override
-    protected AbstractShiroFilter createInstance() throws Exception
-    {
+    protected AbstractShiroFilter createInstance() throws Exception {
 
         SecurityManager securityManager = getSecurityManager();
-        if (securityManager == null)
-        {
+        if (securityManager == null) {
             String msg = "SecurityManager property must be set.";
             throw new BeanInitializationException(msg);
         }
 
-        if (!(securityManager instanceof WebSecurityManager))
-        {
+        if (!(securityManager instanceof WebSecurityManager)) {
             String msg = "The security manager does not implement the WebSecurityManager interface.";
             throw new BeanInitializationException(msg);
         }
@@ -52,8 +48,7 @@ public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean
 
         Map<String, Filter> filterMap = manager.getFilters();
         Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name());
-        if (invalidRequestFilter instanceof InvalidRequestFilter)
-        {
+        if (invalidRequestFilter instanceof InvalidRequestFilter) {
             // 此处是关键,设置false跳过URL携带中文400,servletPath中文校验bug
             ((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false);
         }
@@ -64,19 +59,13 @@ public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean
         return new MySpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
     }
 
-    private static final class MySpringShiroFilter extends AbstractShiroFilter
-    {
-        protected MySpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver)
-        {
-            if (webSecurityManager == null)
-            {
+    private static final class MySpringShiroFilter extends AbstractShiroFilter {
+        protected MySpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
+            if (webSecurityManager == null) {
                 throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
-            }
-            else
-            {
+            } else {
                 this.setSecurityManager(webSecurityManager);
-                if (resolver != null)
-                {
+                if (resolver != null) {
                     this.setFilterChainResolver(resolver);
                 }
             }

+ 54 - 0
qmjszx-framework/src/main/java/beilv/framework/shiro/web/filter/wxMiniAppAccessControlFilter/WxMiniAppAccessControlFilter.java

@@ -0,0 +1,54 @@
+package beilv.framework.shiro.web.filter.wxMiniAppAccessControlFilter;
+
+import beilv.common.utils.rsa.RsaUtil;
+import beilv.system.domain.SysMember;
+import beilv.system.mapper.SysMemberMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.shiro.web.filter.AccessControlFilter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Slf4j
+@Component
+public class WxMiniAppAccessControlFilter extends AccessControlFilter {
+
+    @Value("${rsa.privateKey}")
+    private String privateKey;
+    private final SysMemberMapper sysMemberMapper;
+
+    public WxMiniAppAccessControlFilter(SysMemberMapper sysMemberMapper) {
+        this.sysMemberMapper = sysMemberMapper;
+    }
+
+    @Override
+    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        // 这里可以添加你的自定义验证逻辑
+        String token = httpRequest.getHeader("accessToken");
+        // 验证 token,返回 true 表示允许访问
+        return token != null && isValidToken(token); // 默认不允许访问
+    }
+
+    @Override
+    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+        HttpServletResponse httpResponse = (HttpServletResponse) response;
+        // 设置未授权的响应状态
+        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        return false; // 拒绝访问
+    }
+
+    private boolean isValidToken(String token) throws Exception {
+        // 在这里实现你的 token 验证逻辑
+        String str = RsaUtil.decrypt(token, privateKey);
+        //根据userId从数据库中查询用户信息,判断用户是否存在,如果不存在,则返回false,表示拒绝访问;如果存在,则返回true,表示放行访问
+        SysMember sysMember = sysMemberMapper.selectSysMemberById(Long.parseLong(str));
+        return ObjectUtils.isNotEmpty(sysMember);
+    }
+
+}

+ 27 - 0
qmjszx-system/src/main/java/beilv/system/domain/vo/AppAuthLoginRespVO.java

@@ -0,0 +1,27 @@
+package beilv.system.domain.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AppAuthLoginRespVO {
+
+    /**
+     * 小程序openId
+     */
+    private String openId;
+
+    /**
+     * 访问令牌
+     */
+    private String accessToken;
+
+
+}

+ 22 - 0
qmjszx-system/src/main/java/beilv/system/domain/vo/AppAuthWeixinMiniAppLoginReqVO.java

@@ -0,0 +1,22 @@
+package beilv.system.domain.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotEmpty;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AppAuthWeixinMiniAppLoginReqVO {
+
+    @NotEmpty(message = "手机 code 不能为空")
+    private String phoneCode;
+
+    @NotEmpty(message = "登录 code 不能为空")
+    private String loginCode;
+
+}

+ 28 - 0
qmjszx-system/src/main/java/beilv/system/service/IAppAuthService.java

@@ -0,0 +1,28 @@
+package beilv.system.service;
+
+
+import beilv.system.domain.SysMember;
+import beilv.system.domain.vo.AppAuthLoginRespVO;
+import beilv.system.domain.vo.AppAuthWeixinMiniAppLoginReqVO;
+
+
+/**
+ * 会员的认证 Service 接口
+ * <p>
+ * 提供用户的账号密码登录、token 的校验等认证相关的功能
+ *
+ * @author yshop
+ */
+public interface IAppAuthService {
+
+
+    /**
+     * 微信小程序的一键登录
+     *
+     * @param reqVO 登录信息
+     * @return 登录结果
+     */
+    AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) throws Exception;
+
+
+}

+ 11 - 0
qmjszx-system/src/main/java/beilv/system/service/ISysMemberService.java

@@ -3,6 +3,7 @@ package beilv.system.service;
 import beilv.system.domain.SysMember;
 import beilv.system.domain.dto.SysMemberDTO;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.List;
 
 /**
@@ -68,4 +69,14 @@ public interface ISysMemberService {
      * @return 结果
      */
     int exchange(SysMemberDTO dto);
+
+
+    /**
+     * 获取登录用户信息
+     * @param request
+     * @return
+     * @throws Exception
+     */
+    SysMember getLoginSysMember(HttpServletRequest request) throws Exception;
+
 }

+ 86 - 0
qmjszx-system/src/main/java/beilv/system/service/impl/AppAuthServiceImpl.java

@@ -0,0 +1,86 @@
+package beilv.system.service.impl;
+
+import beilv.common.enums.LoginTypeEnum;
+import beilv.common.utils.rsa.RsaUtil;
+import beilv.system.domain.SysMember;
+import beilv.system.domain.vo.AppAuthLoginRespVO;
+import beilv.system.domain.vo.AppAuthWeixinMiniAppLoginReqVO;
+import beilv.system.mapper.SysMemberMapper;
+import beilv.system.service.IAppAuthService;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+import static beilv.common.enums.ErrorCodeConstants.AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR;
+import static beilv.common.enums.ErrorCodeConstants.MINI_AUTH_LOGIN_BAD;
+import static beilv.common.servlet.ServletUtils.getClientIP;
+import static beilv.common.utils.ExceptionUtil.exception;
+
+
+/**
+ * 会员的认证 Service 接口
+ *
+ * @author yshop
+ */
+@Service
+@Slf4j
+public class AppAuthServiceImpl implements IAppAuthService {
+
+    @Resource
+    private WxMaService wxMaService;
+    @Autowired
+    private SysMemberMapper sysMemberMapper;
+    @Value("${rsa.publicKey}")
+    private String publicKey;
+
+
+    @Override
+    public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) throws Exception {
+        // 获得对应的手机号信息
+        WxMaPhoneNumberInfo phoneNumberInfo;
+        try {
+            phoneNumberInfo = wxMaService.getUserService().getNewPhoneNoInfo(reqVO.getPhoneCode());
+        } catch (Exception exception) {
+            log.error(exception.getMessage());
+            throw exception(AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
+        }
+        WxMaJscode2SessionResult session;
+        try {
+            session = wxMaService.getUserService().getSessionInfo(reqVO.getLoginCode());
+        } catch (WxErrorException e) {
+            log.error(e.getMessage());
+            throw exception(MINI_AUTH_LOGIN_BAD);
+        }
+        // 用户已经存在
+        SysMember memberUserDO = sysMemberMapper.selectByMobile(phoneNumberInfo.getPhoneNumber());
+        if (memberUserDO == null) {
+            // 获得获得注册用户
+            memberUserDO = sysMemberMapper.createUserIfAbsent(phoneNumberInfo.getPhoneNumber(), getClientIP(),
+                    LoginTypeEnum.WXAPP.getValue());
+            memberUserDO.setRealName("用户_" + memberUserDO.getId());
+
+        }
+        // 使用 sessionKey 和 openid 进行用户登录
+        String openId = session.getOpenid();
+        String sessionKey = session.getSessionKey();
+
+        memberUserDO.setOpenId(session.getOpenid());
+        sysMemberMapper.updateSysMember(memberUserDO);
+
+        //加密
+        String accessToken = RsaUtil.encrypt(memberUserDO.getId().toString(), publicKey);
+
+        AppAuthLoginRespVO userInfo = new AppAuthLoginRespVO();
+        userInfo.setAccessToken(accessToken);
+        userInfo.setOpenId(openId);
+        return userInfo;
+    }
+
+}

+ 22 - 0
qmjszx-system/src/main/java/beilv/system/service/impl/SysMemberServiceImpl.java

@@ -2,14 +2,18 @@ package beilv.system.service.impl;
 
 import beilv.common.core.text.Convert;
 import beilv.common.utils.DateUtils;
+import beilv.common.utils.rsa.RsaUtil;
 import beilv.system.domain.SysMember;
 import beilv.system.domain.dto.SysMemberDTO;
 import beilv.system.mapper.SysMemberMapper;
 import beilv.system.service.ISysMemberService;
 import beilv.system.service.ISysUserBillService;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -27,6 +31,8 @@ public class SysMemberServiceImpl implements ISysMemberService {
     private SysMemberMapper sysMemberMapper;
     @Autowired
     private ISysUserBillService sysUserBillService;
+    @Value("${rsa.privateKey}")
+    private String privateKey;
 
     /**
      * 查询会员用户信息
@@ -106,4 +112,20 @@ public class SysMemberServiceImpl implements ISysMemberService {
         sysMember.setIntegral(surplusIntegral);
         return sysMemberMapper.updateSysMember(sysMember);
     }
+
+
+    /**
+     * 获取登录用户信息
+     * @param request
+     * @return
+     * @throws Exception
+     */
+    @Override
+    public SysMember getLoginSysMember(HttpServletRequest request) throws Exception {
+        String token = request.getHeader("accessToken");
+        String str = RsaUtil.decrypt(token, privateKey);
+        return sysMemberMapper.selectSysMemberById(Long.parseLong(str));
+    }
+
+
 }