فهرست منبع

网关统一鉴权

by su 3 سال پیش
والد
کامیت
8f15c90817
28فایلهای تغییر یافته به همراه1068 افزوده شده و 21 حذف شده
  1. 1 1
      4dkankan-center-platform/src/main/java/com/fdkankan/agent/controller/AgentController.java
  2. 10 9
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/SceneApplication.java
  3. 16 0
      4dkankan-center-scene/src/main/resources/key/private_pkcs8.pem
  4. 6 0
      4dkankan-center-scene/src/main/resources/key/public.pem
  5. 20 0
      4dkankan-common/pom.xml
  6. 24 0
      4dkankan-common/src/main/java/com/fdkankan/common/config/RedisConfig.java
  7. 95 0
      4dkankan-common/src/main/java/com/fdkankan/common/config/ShiroConfig.java
  8. 40 0
      4dkankan-common/src/main/java/com/fdkankan/common/config/ShiroModularRealmAuthenticator.java
  9. 26 0
      4dkankan-common/src/main/java/com/fdkankan/common/constant/FilterConstant.java
  10. 18 0
      4dkankan-common/src/main/java/com/fdkankan/common/constant/LoginType.java
  11. 7 0
      4dkankan-common/src/main/java/com/fdkankan/common/constant/RedisKey.java
  12. 5 0
      4dkankan-common/src/main/java/com/fdkankan/common/constant/ServerCode.java
  13. 34 0
      4dkankan-common/src/main/java/com/fdkankan/common/exception/JwtAuthenticationException.java
  14. 115 0
      4dkankan-common/src/main/java/com/fdkankan/common/jwt/JwtFilter.java
  15. 33 0
      4dkankan-common/src/main/java/com/fdkankan/common/jwt/JwtToken.java
  16. 60 0
      4dkankan-common/src/main/java/com/fdkankan/common/realm/AgentJwtRealm.java
  17. 61 0
      4dkankan-common/src/main/java/com/fdkankan/common/realm/AppJwtRealm.java
  18. 61 0
      4dkankan-common/src/main/java/com/fdkankan/common/realm/ManagerJwtRealm.java
  19. 82 0
      4dkankan-common/src/main/java/com/fdkankan/common/realm/UserJwtRealm.java
  20. 126 0
      4dkankan-common/src/main/java/com/fdkankan/common/util/JwtUtil.java
  21. 21 0
      4dkankan-common/src/main/java/com/fdkankan/common/util/SsoUtil.java
  22. 0 0
      4dkankan-common/src/main/resources/file.txt
  23. 1 4
      4dkankan-gateway/src/main/java/com/fdkankan/gateway/GatewayApplication.java
  24. 5 5
      4dkankan-gateway/src/main/java/com/fdkankan/gateway/config/DynamicRouteServiceImplByNacos.java
  25. 1 1
      4dkankan-gateway/src/main/java/com/fdkankan/gateway/config/GatewayConfig.java
  26. 1 1
      4dkankan-gateway/src/main/java/com/fdkankan/gateway/exception/JsonErrorWebExceptionHandler.java
  27. 174 0
      4dkankan-gateway/src/main/java/com/fdkankan/gateway/filter/TokenFilter.java
  28. 25 0
      pom.xml

+ 1 - 1
4dkankan-center-platform/src/main/java/com/fdkankan/agent/controller/AgentController.java

@@ -50,7 +50,7 @@ public class AgentController {
      * @return
      * @return
      */
      */
     @PostMapping(value = "/notice")
     @PostMapping(value = "/notice")
-    public ResultData notice(HttpServletRequest request ) throws Exception {
+    public ResultData notice(HttpServletRequest request) throws Exception {
         AgentNoticeVo agentNoticeVo = new AgentNoticeVo();
         AgentNoticeVo agentNoticeVo = new AgentNoticeVo();
         LinkedHashMap<String,String> queryMap = new LinkedHashMap<>();
         LinkedHashMap<String,String> queryMap = new LinkedHashMap<>();
         queryMap.put("rec_status = A" ,"and");
         queryMap.put("rec_status = A" ,"and");

+ 10 - 9
4dkankan-center-scene/src/main/java/com/fdkankan/scene/SceneApplication.java

@@ -1,6 +1,8 @@
 package com.fdkankan.scene;
 package com.fdkankan.scene;
 
 
+import com.fdkankan.common.config.ShiroConfig;
 import com.fdkankan.common.exception.GlobalExceptionHandler;
 import com.fdkankan.common.exception.GlobalExceptionHandler;
+import com.fdkankan.common.jwt.JwtFilter;
 import com.fdkankan.common.response.ResultData;
 import com.fdkankan.common.response.ResultData;
 import com.fdkankan.scene.dto.TestDto;
 import com.fdkankan.scene.dto.TestDto;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.SpringApplication;
@@ -21,16 +23,15 @@ public class SceneApplication {
         SpringApplication.run(SceneApplication.class, args);
         SpringApplication.run(SceneApplication.class, args);
     }
     }
 
 
-    /**
-     * 注入全局异常处理器
-     */
-    @Bean
-    public GlobalExceptionHandler globalExceptionHandler(){
-        return new GlobalExceptionHandler();
-    }
-
+//    /**
+//     * 注入全局异常处理器
+//     */
+//    @Bean
+//    public GlobalExceptionHandler globalExceptionHandler(){
+//        return new GlobalExceptionHandler();
+//    }
 
 
-    @PostMapping("/test/test")
+    @PostMapping("/api/user/test/test")
     public ResultData test(@RequestBody TestDto test){
     public ResultData test(@RequestBody TestDto test){
         System.out.println("进入了");
         System.out.println("进入了");
         if(test.getName().equals("aaa")){
         if(test.getName().equals("aaa")){

+ 16 - 0
4dkankan-center-scene/src/main/resources/key/private_pkcs8.pem

@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOScGE5AR1jixcGo
+HZMd0kySDk4BQarT1rbYTdXFKXmjn5cXnqGFEfCWnD+bSdLi7fyZdrPso8HjMELo
+vPhBdGmcLwSv74/OT8LnXb1nBZV0uvNe8euFLO3VSUJbOi05z4uTBLsiIJRIpMsU
+j43UpEmgVQvTtmDV23ddhbFMJKUxAgMBAAECgYEAjA6R2YcJ7wvn6jAsBlCz4ZNt
+zh5488aa+iRk8z26njo/cNrYpc4X4pTzHwjtXpUHBsWicrLc1NFyZNufwlyfi1eK
+AaoNC2dHghapmahzCdKRbozVA0+1zsGsUPCNXwPM58YHUtax+nSem1gQJME5+XAA
+A3B1HZSswmiYd6KkBm0CQQD+L1TpsIBpGFWAwvznfHymvifYRzSZzY+Jw110vpt2
+rgPCi77emlBqUsZ5q0VLYaqwuNUjcQp4DB2Ur+7+E7+7AkEA5j4CkmYsb4iIgPiQ
+urmpHrYmIfl8/U6pEaENPDyqzkhyD2IfRh1AqFQfTZzCYlzG7QzzNYY2VCeWMbam
+PMbSAwJBAIW7AUGxyt2AH/wOmmBv7mULxorTa6k/rTUdxB2X6tMV2oSQYWtcZNKL
+mkCJC3wNdnBjzj9HSeq+9RE1PtgOK+ECQQDO4v+an1bkbbIC+dfLAZN29kOzmaR3
+VRxUq/vhmb39mFlte0UQ0q5L0zNBoU+F5UoWreFIwi3FVsD+MNqVmC4TAkEArOZg
+qpGu5SgVNcZKQy0KdBQZoYafD9px+UY0KOo9n08l89DiW63YjrZfcOAf68CBV43R
+t+NFRAUK59TFqvNF6w==
+-----END PRIVATE KEY-----

+ 6 - 0
4dkankan-center-scene/src/main/resources/key/public.pem

@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDknBhOQEdY4sXBqB2THdJMkg5O
+AUGq09a22E3VxSl5o5+XF56hhRHwlpw/m0nS4u38mXaz7KPB4zBC6Lz4QXRpnC8E
+r++Pzk/C5129ZwWVdLrzXvHrhSzt1UlCWzotOc+LkwS7IiCUSKTLFI+N1KRJoFUL
+07Zg1dt3XYWxTCSlMQIDAQAB
+-----END PUBLIC KEY-----

+ 20 - 0
4dkankan-common/pom.xml

@@ -46,6 +46,26 @@
             <artifactId>fastjson</artifactId>
             <artifactId>fastjson</artifactId>
         </dependency>
         </dependency>
 
 
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-spring</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
     </dependencies>
     </dependencies>
 
 
     <build>
     <build>

+ 24 - 0
4dkankan-common/src/main/java/com/fdkankan/common/config/RedisConfig.java

@@ -0,0 +1,24 @@
+package com.fdkankan.common.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+@Configuration
+public class RedisConfig {
+
+    @Bean
+    public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
+        RedisTemplate<String,String>template=new RedisTemplate<>();
+        //关联
+        template.setConnectionFactory(factory);
+        //设置key的序列化器
+        template.setKeySerializer(new StringRedisSerializer());
+        //设置value的序列化器
+        template.setValueSerializer(new StringRedisSerializer());
+        return template;
+    }
+
+}

+ 95 - 0
4dkankan-common/src/main/java/com/fdkankan/common/config/ShiroConfig.java

@@ -0,0 +1,95 @@
+package com.fdkankan.common.config;
+
+import com.fdkankan.common.constant.FilterConstant;
+import com.fdkankan.common.constant.LoginType;
+import com.fdkankan.common.jwt.JwtFilter;
+import com.fdkankan.common.realm.AgentJwtRealm;
+import com.fdkankan.common.realm.AppJwtRealm;
+import com.fdkankan.common.realm.ManagerJwtRealm;
+import com.fdkankan.common.realm.UserJwtRealm;
+import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
+import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
+import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
+import org.apache.shiro.mgt.DefaultSubjectDAO;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.realm.Realm;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.servlet.Filter;
+import java.util.*;
+
+@Configuration
+public class ShiroConfig {
+
+    @Bean("shiroFilter")
+    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
+         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
+        shiroFilterFactoryBean.setSecurityManager(securityManager);
+        //拦截器
+        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
+        // 配置不会被拦截的链接 顺序判断
+//        filterChainDefinitionMap.put("/**", "anon");
+
+        // 添加自己的过滤器并且取名为jwt
+        Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
+        filterMap.put("user_jwt", new JwtFilter(LoginType.USER.toString()));
+        filterMap.put("manager_jwt", new JwtFilter(LoginType.MANAGER.toString()));
+        filterMap.put("agent_jwt", new JwtFilter(LoginType.AGENT.toString()));
+        filterMap.put("app_jwt", new JwtFilter(LoginType.APP.toString()));
+        shiroFilterFactoryBean.setFilters(filterMap);
+        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边
+        filterChainDefinitionMap.put(FilterConstant.FILTER_USER_URL + "/**", "user_jwt");
+        filterChainDefinitionMap.put(FilterConstant.FILTER_SCENE_URL + "/**", "user_jwt");
+        filterChainDefinitionMap.put(FilterConstant.FILTER_DEVICE_URL + "/**", "user_jwt");
+        filterChainDefinitionMap.put(FilterConstant.FILTER_PAY_URL + "/**", "user_jwt");
+        filterChainDefinitionMap.put(FilterConstant.FILTER_MANAGER_URL + "/**", "manager_jwt");
+        filterChainDefinitionMap.put(FilterConstant.FILTER_AGENT_URL + "/**", "agent_jwt");
+        filterChainDefinitionMap.put(FilterConstant.FILTER_APP_URL + "/**", "app_jwt");
+        //未授权界面;
+        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
+        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
+
+        return shiroFilterFactoryBean;
+    }
+
+    @Bean("securityManager")
+    public SecurityManager securityManager() {
+        List<Realm> realms = new ArrayList<>();
+        realms.add(new UserJwtRealm());
+        realms.add(new AgentJwtRealm());
+        realms.add(new ManagerJwtRealm());
+        realms.add(new AppJwtRealm());
+
+        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+        //设置realm.
+        securityManager.setAuthenticator(modularRealmAuthenticator());
+        securityManager.setRealms(realms);
+        /*
+         * 关闭shiro自带的session,详情见文档
+         * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
+         */
+        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
+        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
+        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
+        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
+        securityManager.setSubjectDAO(subjectDAO);
+
+        return securityManager;
+    }
+
+    @Bean
+    public ModularRealmAuthenticator modularRealmAuthenticator(){
+        //自己重写的ShiroModularRealmAuthenticator
+        ShiroModularRealmAuthenticator modularRealmAuthenticator = new ShiroModularRealmAuthenticator();
+        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
+        return modularRealmAuthenticator;
+    }
+
+
+
+
+
+}

+ 40 - 0
4dkankan-common/src/main/java/com/fdkankan/common/config/ShiroModularRealmAuthenticator.java

@@ -0,0 +1,40 @@
+package com.fdkankan.common.config;
+
+import com.fdkankan.common.jwt.JwtToken;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
+import org.apache.shiro.realm.Realm;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class ShiroModularRealmAuthenticator extends ModularRealmAuthenticator {
+
+    @Override
+    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
+            throws AuthenticationException {
+        // 判断getRealms()是否返回为空
+        assertRealmsConfigured();
+        // 强制转换回自定义的CustomizedToken
+        JwtToken customizedToken = (JwtToken) authenticationToken;
+        // 登录类型
+        String loginType = customizedToken.getLoginType();
+        // 所有Realm
+        Collection<Realm> realms = getRealms();
+        // 登录类型对应的所有Realm
+        Collection<Realm> typeRealms = new ArrayList<>();
+        for (Realm realm : realms) {
+            if (realm.getName().contains(loginType))
+                typeRealms.add(realm);
+        }
+
+        // 判断是单Realm还是多Realm
+        if (typeRealms.size() == 1)
+            return doSingleRealmAuthentication(typeRealms.iterator().next(), customizedToken);
+        else
+            return doMultiRealmAuthentication(typeRealms, customizedToken);
+    }
+
+}

+ 26 - 0
4dkankan-common/src/main/java/com/fdkankan/common/constant/FilterConstant.java

@@ -0,0 +1,26 @@
+package com.fdkankan.common.constant;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FilterConstant {
+
+    public static final String FILTER_USER_URL = "/api/user";
+    public static final String FILTER_SCENE_URL = "/api/scene/edit";
+    public static final String FILTER_DEVICE_URL = "/api/device";
+    public static final String FILTER_PAY_URL = "/api/order/scanPay";
+    public static final String FILTER_MANAGER_URL = "/api/manager";
+    public static final String FILTER_AGENT_URL = "/api/agent";
+    public static final String FILTER_APP_URL = "/api/app";
+
+    public static List<String> getFilterPattern(){
+        List<String> arr = new ArrayList<>();
+        arr.add(FILTER_USER_URL);
+        arr.add(FILTER_SCENE_URL);
+        arr.add(FILTER_DEVICE_URL);
+        arr.add(FILTER_PAY_URL);
+//        arr.add(FILTER_MANAGER_URL);
+//        arr.add(FILTER_AGENT_URL);
+        return arr;
+    }
+}

+ 18 - 0
4dkankan-common/src/main/java/com/fdkankan/common/constant/LoginType.java

@@ -0,0 +1,18 @@
+package com.fdkankan.common.constant;
+
+public enum LoginType {
+
+    USER("User"),  MANAGER("Manager"), AGENT("Agent"), APP("App");
+
+    private String type;
+
+    private LoginType(String type) {
+        this.type = type;
+    }
+
+    @Override
+    public String toString() {
+        return this.type.toString();
+    }
+
+}

+ 7 - 0
4dkankan-common/src/main/java/com/fdkankan/common/constant/RedisKey.java

@@ -0,0 +1,7 @@
+package com.fdkankan.common.constant;
+
+public class RedisKey {
+
+    public static String TOKEN_USER_TYPE = "token:user:type:%s:%s";
+
+}

+ 5 - 0
4dkankan-common/src/main/java/com/fdkankan/common/constant/ServerCode.java

@@ -5,6 +5,11 @@ public enum ServerCode {
 	SUCCESS(0, "成功"),
 	SUCCESS(0, "成功"),
 	SYSTEM_ERROR(-1, "服务器异常"),
 	SYSTEM_ERROR(-1, "服务器异常"),
 	PARAM_ERROR(-2, "解析请求参数出错"),
 	PARAM_ERROR(-2, "解析请求参数出错"),
+
+	AUTH_FAIL(3000, "鉴权失败!"),
+	NON_TOKEN(3001, "无token,请重新登录"),
+	TOKEN_ILLEGAL(3002, "token不合法"),
+	TOKEN_NOT_FOUND(3003, "用户未登录"),
 	;
 	;
 	
 	
 	private Integer code;
 	private Integer code;

+ 34 - 0
4dkankan-common/src/main/java/com/fdkankan/common/exception/JwtAuthenticationException.java

@@ -0,0 +1,34 @@
+package com.fdkankan.common.exception;
+
+import org.apache.shiro.ShiroException;
+
+public class JwtAuthenticationException extends ShiroException {
+
+    private static final long serialVersionUID = 2899335020273674736L;
+
+    private int code;
+
+    private String msg;
+
+    public JwtAuthenticationException(int code, String msg){
+        super(msg);
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+}

+ 115 - 0
4dkankan-common/src/main/java/com/fdkankan/common/jwt/JwtFilter.java

@@ -0,0 +1,115 @@
+package com.fdkankan.common.jwt;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fdkankan.common.exception.JwtAuthenticationException;
+import lombok.extern.log4j.Log4j2;
+import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+@Log4j2
+public class JwtFilter extends BasicHttpAuthenticationFilter {
+
+    private String loginType;
+
+    public JwtFilter(String loginType){
+        this.loginType = loginType;
+    }
+
+    /**
+     * 执行登录认证
+     *
+     * @param request
+     * @param response
+     * @param mappedValue
+     * @return
+     */
+    @Override
+    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+        try {
+            log.info("Step 1: 接口拦截shiro认证权限");
+            executeLogin(request, response);
+            String url = ((HttpServletRequest) request).getRequestURI();
+//            getSubject(request, response).checkPermission(url);
+            return true;
+        } catch (Exception e) {
+            // 认证出现异常,传递错误信息msg
+            String msg = e.getMessage();
+            // 获取应用异常(该Cause是导致抛出此throwable(异常)的throwable(异常))
+            Throwable throwable = e.getCause();
+            JSONObject jsonObject = new JSONObject();
+            if (throwable instanceof JwtAuthenticationException) {
+                jsonObject.put("code", ((JwtAuthenticationException) throwable).getCode());
+                jsonObject.put("msg", ((JwtAuthenticationException) throwable).getMsg());
+            }else{
+                jsonObject.put("code", 3002);
+                jsonObject.put("msg", msg);
+            }
+            // 直接返回Response信息
+            this.response(response, jsonObject);
+            return false;
+        }
+    }
+
+    /**
+     *
+     */
+    @Override
+    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
+        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+        String token = httpServletRequest.getHeader("token");
+
+        JwtToken jwtToken = new JwtToken(token, loginType);
+        // 提交给realm进行登入,如果错误他会抛出异常并被捕获
+        getSubject(request, response).login(jwtToken);
+        // 如果没有抛出异常则代表登入成功,返回true
+        return true;
+    }
+
+    /**
+     * 对跨域提供支持
+     */
+    @Override
+    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
+        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
+        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
+        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
+        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
+        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
+            httpServletResponse.setStatus(HttpStatus.OK.value());
+            return false;
+        }
+        return super.preHandle(request, response);
+    }
+
+    /**
+     * 无需转发,直接返回Response信息
+     */
+    private void response(ServletResponse response, JSONObject msg) {
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/json; charset=utf-8");
+        PrintWriter out = null;
+        try {
+            out = response.getWriter();
+            out.append(msg.toString());
+            out.flush();
+            out.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (out != null){
+                out.close();
+            }
+        }
+    }
+
+}

+ 33 - 0
4dkankan-common/src/main/java/com/fdkankan/common/jwt/JwtToken.java

@@ -0,0 +1,33 @@
+package com.fdkankan.common.jwt;
+
+import org.apache.shiro.authc.AuthenticationToken;
+
+public class JwtToken implements AuthenticationToken {
+
+    private String token;
+
+    private String loginType;
+
+    public JwtToken(String token, String loginType){
+        this.token = token;
+        this.loginType = loginType;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return token;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return token;
+    }
+
+    public String getLoginType() {
+        return loginType;
+    }
+
+    public void setLoginType(String loginType) {
+        this.loginType = loginType;
+    }
+}

+ 60 - 0
4dkankan-common/src/main/java/com/fdkankan/common/realm/AgentJwtRealm.java

@@ -0,0 +1,60 @@
+package com.fdkankan.common.realm;
+
+import cn.hutool.core.util.StrUtil;
+import com.fdkankan.common.exception.JwtAuthenticationException;
+import com.fdkankan.common.jwt.JwtToken;
+import com.fdkankan.common.util.JwtUtil;
+import com.fdkankan.common.util.SsoUtil;
+import lombok.extern.log4j.Log4j2;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.springframework.stereotype.Component;
+
+@Log4j2
+@Component
+public class AgentJwtRealm extends AuthorizingRealm {
+
+    @Override
+    public boolean supports(AuthenticationToken token) {
+        return token instanceof JwtToken;
+    }
+
+    @Override
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
+        return simpleAuthorizationInfo;
+    }
+
+    @Override
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
+        log.info("Step 2: Agent进行用户名正确与否验证");
+        String token = (String) auth.getCredentials();
+        if (StrUtil.isEmpty(token)){
+            throw new JwtAuthenticationException(3004, "无token,请重新登录");
+        }
+        // 获取 token 中的 userName
+        String userName = JwtUtil.getUsername(token);
+        if (StrUtil.isEmpty(userName)){
+            throw new JwtAuthenticationException(3004, "访问异常!");
+        }
+        if (!userName.contains(SsoUtil.PREFIX_CACHE_AGENT)){
+            throw new JwtAuthenticationException(3004, "用户未登录");
+        }
+        if (!JwtUtil.isVerify(token, userName)) {
+            throw new JwtAuthenticationException(3004, "非法访问!");
+        }
+//        // 防止重复登陆, redisToken是最新登录获取的token
+//        String redisToken = JedisUtil.getStringValue(userName);
+//        if (StrUtil.isEmpty(redisToken) || !redisToken.equals(token)){
+//            throw new JwtAuthenticationException(3004, "用户未登录");
+//        }
+
+        return new SimpleAuthenticationInfo(token, token, "jwt_realm");
+    }
+}

+ 61 - 0
4dkankan-common/src/main/java/com/fdkankan/common/realm/AppJwtRealm.java

@@ -0,0 +1,61 @@
+package com.fdkankan.common.realm;
+
+import cn.hutool.core.util.StrUtil;
+import com.fdkankan.common.exception.JwtAuthenticationException;
+import com.fdkankan.common.jwt.JwtToken;
+import com.fdkankan.common.util.JwtUtil;
+import lombok.extern.log4j.Log4j2;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.springframework.stereotype.Component;
+
+@Log4j2
+@Component
+public class AppJwtRealm extends AuthorizingRealm {
+
+    @Override
+    public boolean supports(AuthenticationToken token) {
+        return token instanceof JwtToken;
+    }
+
+    @Override
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
+        return simpleAuthorizationInfo;
+    }
+
+    @Override
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
+        log.info("Step 2: Agent进行用户名正确与否验证");
+        String token = (String) auth.getCredentials();
+        log.info("doGetAuthenticationInfo - step3 :" + token);
+        if (StrUtil.isEmpty(token)){
+            throw new JwtAuthenticationException(3004, "无token,请重新登录");
+        }
+        // 获取 token 中的 userName
+        String userName = JwtUtil.getUsername(token);
+        if (StrUtil.isEmpty(userName)){
+            throw new JwtAuthenticationException(3004, "访问异常!");
+        }
+        String regex = "^-?\\d+(\\.\\d+)?$";
+        if(userName.matches(regex)){
+            // TODO: 2021/12/21
+//            if (!JedisUtil.exists(SsoUtil.PREFIX_CACHE_CAMERA + userName)){
+//                throw new JwtAuthenticationException(3004, "用户未登录");
+//            }
+//            if (!JedisUtil.getStringValue(SsoUtil.PREFIX_CACHE_CAMERA + userName).contains(token)){
+//                throw new JwtAuthenticationException(3004, "用户未登录");
+//            }
+        }
+// TODO: 2021/12/21
+//        JedisUtil.expire(SsoUtil.PREFIX_CACHE_CAMERA + userName, 21600);
+
+        return new SimpleAuthenticationInfo(token, token, "jwt_realm");
+    }
+}

+ 61 - 0
4dkankan-common/src/main/java/com/fdkankan/common/realm/ManagerJwtRealm.java

@@ -0,0 +1,61 @@
+package com.fdkankan.common.realm;
+
+import cn.hutool.core.util.StrUtil;
+import com.fdkankan.common.exception.JwtAuthenticationException;
+import com.fdkankan.common.jwt.JwtToken;
+import com.fdkankan.common.util.JwtUtil;
+import com.fdkankan.common.util.SsoUtil;
+import lombok.extern.log4j.Log4j2;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.springframework.stereotype.Component;
+
+@Log4j2
+@Component
+public class ManagerJwtRealm extends AuthorizingRealm {
+
+    @Override
+    public boolean supports(AuthenticationToken token) {
+        return token instanceof JwtToken;
+    }
+
+    @Override
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
+        return simpleAuthorizationInfo;
+    }
+
+    @Override
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
+        log.info("Step 2: Manager进行用户名正确与否验证");
+        String token = (String) auth.getCredentials();
+        if (StrUtil.isEmpty(token)){
+            throw new JwtAuthenticationException(3004, "无token,请重新登录");
+        }
+        // 获取 token 中的 userName
+        String userName = JwtUtil.getUsername(token);
+        if (StrUtil.isEmpty(userName)){
+            throw new JwtAuthenticationException(3004, "访问异常!");
+        }
+        if (!userName.contains(SsoUtil.PREFIX_CACHE_MANAGER)){
+            throw new JwtAuthenticationException(3004, "用户未登录");
+        }
+        if (!JwtUtil.isVerify(token, userName)) {
+            throw new JwtAuthenticationException(3004, "非法访问!");
+        }
+        // 防止重复登陆, redisToken是最新登录获取的token
+        // TODO: 2021/12/21  
+//        String redisToken = JedisUtil.getStringValue(userName);
+//        if (StrUtil.isEmpty(redisToken) || !redisToken.equals(token)){
+//            throw new JwtAuthenticationException(3004, "用户未登录");
+//        }
+
+        return new SimpleAuthenticationInfo(token, token, "jwt_realm");
+    }
+}

+ 82 - 0
4dkankan-common/src/main/java/com/fdkankan/common/realm/UserJwtRealm.java

@@ -0,0 +1,82 @@
+package com.fdkankan.common.realm;
+
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fdkankan.common.exception.JwtAuthenticationException;
+import com.fdkankan.common.jwt.JwtToken;
+import com.fdkankan.common.util.JwtUtil;
+import lombok.extern.log4j.Log4j2;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Log4j2
+@Component
+public class UserJwtRealm extends AuthorizingRealm {
+
+//    @Autowired
+//    private UserFeignClient userService;
+    @Autowired
+    private ObjectMapper mapper;
+
+    /**
+     * 必须重写此方法,不然Shiro会报错
+     */
+    @Override
+    public boolean supports(AuthenticationToken token) {
+        return token instanceof JwtToken;
+    }
+    /**
+     * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
+     */
+    @Override
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
+        String username = (String)principals.getPrimaryPrincipal();
+//        List list = principals.asList();
+//        Result result = userService.findByUserName(username);
+//        if (result.getCode() == Result.CODE_FAILURE){
+//            return authorizationInfo;
+//        }
+//        SSOUser dbUser = mapper.convertValue(result.getData(), SSOUser.class);
+//        authorizationInfo.setRoles(dbUser.getRoleSet());
+//        authorizationInfo.setStringPermissions(dbUser.getPermissionSet());
+        return authorizationInfo;
+    }
+    /**
+     * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
+     */
+    @Override
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
+        log.info("Step 2: User进行用户名正确与否验证");
+        String token = (String) auth.getCredentials();
+        if (StrUtil.isEmpty(token)){
+            throw new JwtAuthenticationException(3004, "无token,请重新登录");
+        }
+        // 解密获得username,用于和数据库进行对比
+        String username = JwtUtil.getUsername(token);
+        if (username == null) {
+            throw new JwtAuthenticationException(3004, "访问异常!");
+        }
+        if (!JwtUtil.isVerify(token, username)) {
+            throw new JwtAuthenticationException(3004, "非法访问!");
+        }
+        // TODO: 2021/12/21
+//        SSOUser ssoUser = SSOLoginHelper.loginCheck(token);
+//        if (ssoUser == null){
+//            throw new JwtAuthenticationException(3004, "用户未登录");
+//        }
+//
+//        // refresh
+        // TODO: 2021/12/21
+//        JedisUtil.expire(token, 21600);
+        return new SimpleAuthenticationInfo(username, token, "jwt_realm");
+    }
+}

+ 126 - 0
4dkankan-common/src/main/java/com/fdkankan/common/util/JwtUtil.java

@@ -0,0 +1,126 @@
+package com.fdkankan.common.util;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class JwtUtil {
+
+    //生成签名的时候使用的秘钥secret
+    private static final String SECRET = "XX#$%()(#*!()!KL<><MQLMNQNQJQK&sdfkjsdrow32234545fdf>?N<:{LWPW";
+
+    /**
+     * 用户登录成功后生成Jwt
+     * 使用Hs256算法  私匙使用用户密码
+     *
+     * @param ttlMillis jwt过期时间
+     * @param userName  用户名
+     * @return
+     */
+    public static String createJWT(long ttlMillis, String userName, String loginType) {
+        //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
+        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+
+        //生成JWT的时间
+        long nowMillis = System.currentTimeMillis();
+        Date now = new Date(nowMillis);
+
+        //创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
+        Map<String, Object> claims = new HashMap<String, Object>();
+        claims.put("userName", userName);
+        claims.put("loginType", loginType);
+
+        //下面就是在为payload添加各种标准声明和私有声明了
+        //这里其实就是new一个JwtBuilder,设置jwt的body
+        JwtBuilder builder = Jwts.builder()
+                //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
+                .setClaims(claims)
+                //设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
+                .setId(UUID.randomUUID().toString())
+                //iat: jwt的签发时间
+                .setIssuedAt(now)
+                //代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
+                .setSubject(userName)
+                //设置签名使用的签名算法和签名使用的秘钥
+                .signWith(signatureAlgorithm, SECRET);
+
+        if (ttlMillis >= 0) {
+            long expMillis = nowMillis + ttlMillis;
+            Date exp = new Date(expMillis);
+            //设置过期时间
+            builder.setExpiration(exp);
+        }/*else{
+            //设置过期时间半小时
+            DateTime expDate = new DateTime().plusMinutes(30);
+            builder.setExpiration(expDate.toDate());
+        }*/
+        return builder.compact();
+    }
+
+
+    /**
+     * Token的解密
+     * @param token 加密后的token
+     * @return
+     */
+    public static Claims parseJWT(String token) {
+        //得到DefaultJwtParser
+        Claims claims = Jwts.parser()
+                //设置签名的秘钥
+                .setSigningKey(SECRET)
+                //设置需要解析的jwt
+                .parseClaimsJws(token).getBody();
+        return claims;
+    }
+
+
+    /**
+     * 校验token
+     * 在这里可以使用官方的校验,我这里校验的是token中携带的密码于数据库一致的话就校验通过
+     * @param token
+     * @param userName
+     * @return
+     */
+    public static Boolean isVerify(String token, String userName) {
+
+        //得到DefaultJwtParser
+        Claims claims = Jwts.parser()
+                //设置签名的秘钥
+                .setSigningKey(SECRET)
+                //设置需要解析的jwt
+                .parseClaimsJws(token).getBody();
+
+        if (claims.get("userName").equals(userName)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 获得token中的信息无需secret解密也能获得
+     *
+     * @return token中包含的用户名
+     */
+    public static String getUsername(String token) {
+        try {
+            DecodedJWT jwt = JWT.decode(token);
+            return jwt.getClaim("userName").asString();
+        } catch (JWTDecodeException e) {
+            return null;
+        }
+    }
+
+    public static void main(String[] args) {
+        System.out.println(getUsername("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMzExMjMxMTE3OCIsInVzZXJOYW1lIjoiMTMxMTIzMTExNzgiLCJpYXQiOjE1NjQwNDY0OTgsImp0aSI6IjhhZWJlNzhlLTU2OGUtNDY2Yi1iY2E3LWQ4ZjI0MjgxYzJhZCJ9.1N3UNjkT39mXtymfkJQVQJxOlFM3gfC6bgfur5mVmEU"));
+    }
+
+}

+ 21 - 0
4dkankan-common/src/main/java/com/fdkankan/common/util/SsoUtil.java

@@ -0,0 +1,21 @@
+package com.fdkankan.common.util;
+
+public class SsoUtil {
+
+    public static final String REDIRECT_URL = "redirect_url";
+    public static final String SSO_USER = "sso_user";
+    public static final String SSO_SERVER = "sso_server";
+    public static final String SSO_LOGIN = "/login";
+    public static final String SSO_LOGOUT = "/logout";
+    public static final String SSO_LOGOUT_PATH = "logoutPath";
+    public static final String PREFIX_SHIRO_CACHE = "shiro:cache:";
+    public static final String PREFIX_MSG_AUTH_CODE = "msg:auth:code:";
+    public static final String PREFIX_CACHE_MANAGER = "manager:";
+    public static final String PREFIX_CACHE_AGENT = "agent:";
+    public static final String PREFIX_CACHE_CAMERA = "camera:";
+    public static final String SSO_SESSIONID = "token";
+    public static final String PREFIX_CACHE_NICKNAME = "nickname:";
+    public static final String PREFIX_MSG_NOT_CODE = "msg:not:code:";//短信重发验证
+
+    public static final String USER_TOKEN = "user:token:";
+}

+ 0 - 0
4dkankan-common/src/main/resources/file.txt


+ 1 - 4
4dkankan-gateway/src/main/java/com/fdkankan/gateway/GatewayApplication.java

@@ -1,15 +1,11 @@
 package com.fdkankan.gateway;
 package com.fdkankan.gateway;
 
 
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
 
 
 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
 @EnableDiscoveryClient
 @EnableDiscoveryClient
@@ -23,6 +19,7 @@ public class GatewayApplication {
             System.out.println(test);
             System.out.println(test);
     }
     }
 
 
+
 //    @RequestMapping("/test/test")
 //    @RequestMapping("/test/test")
 //    @ResponseBody
 //    @ResponseBody
 //    public String test(){
 //    public String test(){

+ 5 - 5
4dkankan-gateway/src/main/java/com/fdkankan/gateway/config/DynamicRouteServiceImplByNacos.java

@@ -23,7 +23,7 @@ import java.util.concurrent.Executor;
  */
  */
 @Component
 @Component
 @Slf4j
 @Slf4j
-@DependsOn({"gatewayConfig"}) // 依赖于gatewayConfig bean
+@DependsOn({"nacosConfig"}) // 依赖于gatewayConfig bean
 public class DynamicRouteServiceImplByNacos {
 public class DynamicRouteServiceImplByNacos {
 
 
     @Autowired
     @Autowired
@@ -40,7 +40,7 @@ public class DynamicRouteServiceImplByNacos {
                 log.warn("initConfigService fail");
                 log.warn("initConfigService fail");
                 return;
                 return;
             }
             }
-            String configInfo = configService.getConfig(GatewayConfig.NACOS_ROUTE_DATA_ID, GatewayConfig.NACOS_ROUTE_GROUP, GatewayConfig.DEFAULT_TIMEOUT);
+            String configInfo = configService.getConfig(NacosConfig.NACOS_ROUTE_DATA_ID, NacosConfig.NACOS_ROUTE_GROUP, NacosConfig.DEFAULT_TIMEOUT);
             log.info("获取网关当前配置:\r\n{}",configInfo);
             log.info("获取网关当前配置:\r\n{}",configInfo);
             List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
             List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
             for(RouteDefinition definition : definitionList){
             for(RouteDefinition definition : definitionList){
@@ -50,7 +50,7 @@ public class DynamicRouteServiceImplByNacos {
         } catch (Exception e) {
         } catch (Exception e) {
             log.error("初始化网关路由时发生错误",e);
             log.error("初始化网关路由时发生错误",e);
         }
         }
-        dynamicRouteByNacosListener(GatewayConfig.NACOS_ROUTE_DATA_ID,GatewayConfig.NACOS_ROUTE_GROUP);
+        dynamicRouteByNacosListener(NacosConfig.NACOS_ROUTE_DATA_ID, NacosConfig.NACOS_ROUTE_GROUP);
     }
     }
 
 
     /**
     /**
@@ -86,8 +86,8 @@ public class DynamicRouteServiceImplByNacos {
     private ConfigService initConfigService(){
     private ConfigService initConfigService(){
         try{
         try{
             Properties properties = new Properties();
             Properties properties = new Properties();
-            properties.setProperty("serverAddr",GatewayConfig.NACOS_SERVER_ADDR);
-            properties.setProperty("namespace",GatewayConfig.NACOS_NAMESPACE);
+            properties.setProperty("serverAddr", NacosConfig.NACOS_SERVER_ADDR);
+            properties.setProperty("namespace", NacosConfig.NACOS_NAMESPACE);
             return configService= NacosFactory.createConfigService(properties);
             return configService= NacosFactory.createConfigService(properties);
         } catch (Exception e) {
         } catch (Exception e) {
             log.error("初始化网关路由时发生错误",e);
             log.error("初始化网关路由时发生错误",e);

+ 1 - 1
4dkankan-gateway/src/main/java/com/fdkankan/gateway/config/GatewayConfig.java

@@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Configuration;
 
 
 @Configuration
 @Configuration
-public class GatewayConfig {
+public class NacosConfig {
 
 
     public static final long DEFAULT_TIMEOUT = 30000;
     public static final long DEFAULT_TIMEOUT = 30000;
 
 

+ 1 - 1
4dkankan-gateway/src/main/java/com/fdkankan/gateway/exception/JsonErrorWebExceptionHandler.java

@@ -29,7 +29,7 @@ public class JsonErrorWebExceptionHandler extends DefaultErrorWebExceptionHandle
         // 这里其实可以根据异常类型进行定制化逻辑
         // 这里其实可以根据异常类型进行定制化逻辑
         Throwable error = super.getError(request);
         Throwable error = super.getError(request);
         Map<String, Object> errorAttributes = new HashMap<>(8);
         Map<String, Object> errorAttributes = new HashMap<>(8);
-        errorAttributes.put("status", ServerCode.SYSTEM_ERROR.code());
+        errorAttributes.put("code", ServerCode.SYSTEM_ERROR.code());
         errorAttributes.put("message", ServerCode.SYSTEM_ERROR.message());
         errorAttributes.put("message", ServerCode.SYSTEM_ERROR.message());
         errorAttributes.put("method", request.methodName());
         errorAttributes.put("method", request.methodName());
         errorAttributes.put("path", request.path());
         errorAttributes.put("path", request.path());

+ 174 - 0
4dkankan-gateway/src/main/java/com/fdkankan/gateway/filter/TokenFilter.java

@@ -0,0 +1,174 @@
+package com.fdkankan.gateway.filter;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.fdkankan.common.constant.RedisKey;
+import com.fdkankan.common.constant.ServerCode;
+import com.fdkankan.common.response.ResultData;
+import com.fdkankan.common.util.JwtUtil;
+import io.jsonwebtoken.Claims;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+@Slf4j
+@Component
+public class TokenFilter implements GlobalFilter, Ordered {
+
+    private static final String TOKEN = "token";
+
+    private static final Map<String, List<String>> permissionMap = new HashMap<>();
+
+    private static final List<String> ignoreAuthUrls = new ArrayList<>();
+
+//    @Resource
+//    private RedisUtil redisUtil;
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    static {
+        List<String> userPermissions = new ArrayList<>();
+        permissionMap.put("user", userPermissions);
+        userPermissions.add("/api/user");
+        userPermissions.add("/api/scene/edit");
+        userPermissions.add("/api/scene/edit");
+        userPermissions.add("/api/order/scanPay");
+
+        List<String> managerPermissions = new ArrayList<>();
+        permissionMap.put("manager", managerPermissions);
+        userPermissions.add("/api/manager");
+
+        List<String> agentPermissions = new ArrayList<>();
+        permissionMap.put("agent", agentPermissions);
+        userPermissions.add("/api/agent");
+
+        List<String> appPermissions = new ArrayList<>();
+        permissionMap.put("app", appPermissions);
+        userPermissions.add("/api/app");
+
+
+        ignoreAuthUrls.add("/api/sso");
+
+    }
+
+
+    /**
+     * 忽略过滤的路径
+     */
+
+//    @Value("${com.cuslink.ignoreAuthUrls}")
+//    private String ignoreAuthUrls;
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        ServerHttpRequest request = exchange.getRequest();
+        ServerHttpResponse response = exchange.getResponse();
+        // 获取当前请求路径
+        String path = request.getURI().getPath();
+        log.info("当前路径为" + path + ",判断是否为忽略路径.");
+        // 查询是否是忽略路径
+        boolean pass = decideIgnore(path);
+        if (pass) {
+            // 忽略路径直接放行
+            log.info("当前路径为" + path + ",是忽略过滤路径,直接放行!");
+            return chain.filter(exchange);
+        } else {
+            ServerCode serverCode = null;
+            log.info("当前路径为" + path + ",不是忽略过滤路径,开始校验!");
+            // 获取当前请求中的token
+            String token = request.getHeaders().getFirst(TOKEN);
+            if(StrUtil.isBlank(token)){
+                serverCode = ServerCode.TOKEN_NOT_FOUND;
+                DataBuffer buffer = setResponseInfo(response, ResultData.error(serverCode.code(), serverCode.message()));
+                return response.writeWith(Mono.just(buffer));
+            }else{
+                //验证token是否被篡改
+                Claims claims = null;
+                try {
+                    claims = JwtUtil.parseJWT(token);
+                }catch (Exception e){
+                    serverCode = ServerCode.TOKEN_ILLEGAL;
+                    DataBuffer buffer = setResponseInfo(response, ResultData.error(serverCode.code(), serverCode.message()));
+                    return response.writeWith(Mono.just(buffer));
+                }
+
+                String userName = (String)claims.get("userName");
+                String loginType = (String)claims.get("loginType");
+
+                //查询redis,token是否存在
+                String key = String.format(RedisKey.TOKEN_USER_TYPE, userName, loginType);
+                boolean exist = redisTemplate.hasKey(key);
+                if(!exist){
+                    DataBuffer buffer = setResponseInfo(response, ResultData.error(ServerCode.TOKEN_NOT_FOUND.code(), ServerCode.TOKEN_NOT_FOUND.message()));
+                    return response.writeWith(Mono.just(buffer));
+                }
+
+                // TODO: 2021/12/21  校验资源权限  用户资源可以从token中取,这里暂时先写死
+                //根据用户ID查询角色列表
+                //根据角色查询可访问资源权限列表
+                //遍历权限列表,对比path,如果匹配上,则放行
+                List<String> permissions = permissionMap.get(loginType);
+                Boolean isPermission = true;
+                for (String permission : permissions) {
+                    if(path.startsWith(permission)){
+                        isPermission = true;
+                        break;
+                    }
+                }
+
+                if(isPermission){
+                    return chain.filter(exchange);
+                }
+            }
+        }
+        DataBuffer buffer = setResponseInfo(response, ResultData.error(ServerCode.AUTH_FAIL.code(), ServerCode.AUTH_FAIL.message()));
+        return response.writeWith(Mono.just(buffer));
+    }
+
+
+    private DataBuffer setResponseInfo(ServerHttpResponse response, ResultData resultData) {
+        response.setStatusCode(HttpStatus.UNAUTHORIZED);
+        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
+        byte[] responseByte = JSONUtil.parse(resultData).toString().getBytes();
+        DataBuffer buffer = response.bufferFactory().wrap(responseByte);
+        return buffer;
+    }
+
+    /**
+     * 判断是否是忽略路径
+     *
+     * @param servletPath
+     * @return
+     */
+    private boolean decideIgnore(String servletPath) {
+        //跳过不需要验证的路径
+        for (String ignore : ignoreAuthUrls) {
+            if (servletPath.startsWith(ignore)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int getOrder() {
+        return -101;
+    }
+
+}

+ 25 - 0
pom.xml

@@ -34,6 +34,9 @@
         <druid-version>1.1.17</druid-version>
         <druid-version>1.1.17</druid-version>
         <spring.plugin.metadata-version>1.2.0.RELEASE</spring.plugin.metadata-version>
         <spring.plugin.metadata-version>1.2.0.RELEASE</spring.plugin.metadata-version>
         <jwt-version>3.10.3</jwt-version>
         <jwt-version>3.10.3</jwt-version>
+        <shiro.version>1.7.1</shiro.version>
+        <version>0.6.0</version>
+        <jjwt.version>0.6.0</jjwt.version>
     </properties>
     </properties>
 
 
     <dependencyManagement>
     <dependencyManagement>
@@ -104,6 +107,28 @@
                 <version>${jwt-version}</version>
                 <version>${jwt-version}</version>
             </dependency>
             </dependency>
 
 
+<!--            <dependency>-->
+<!--                <groupId>org.apache.shiro</groupId>-->
+<!--                <artifactId>shiro-core</artifactId>-->
+<!--                <version>${shiro.version}</version>-->
+<!--            </dependency>-->
+            <dependency>
+                <groupId>org.apache.shiro</groupId>
+                <artifactId>shiro-spring</artifactId>
+                <version>${shiro.version}</version>
+            </dependency>
+<!--            <dependency>-->
+<!--                <groupId>org.apache.shiro</groupId>-->
+<!--                <artifactId>shiro-web</artifactId>-->
+<!--                <version>${shiro.version}</version>-->
+<!--            </dependency>-->
+
+            <dependency>
+                <groupId>io.jsonwebtoken</groupId>
+                <artifactId>jjwt</artifactId>
+                <version>${jjwt.version}</version>
+            </dependency>
+
         </dependencies>
         </dependencies>
     </dependencyManagement>
     </dependencyManagement>