Selaa lähdekoodia

fix:修复shiro的session任然生效问题(用jwt做登录认证不需要session状态)

wangzaijun 7 kuukautta sitten
vanhempi
commit
0524155f9a

+ 34 - 27
service-base/src/main/java/com/simuwang/base/config/ShiroConfig.java

@@ -5,26 +5,25 @@ import com.simuwang.base.components.ShiroLoginAuthAdapter;
 import com.simuwang.base.components.ShiroRsaCredentialsMatcher;
 import com.simuwang.base.components.UserAuthService;
 import com.simuwang.shiro.core.ShiroDbRealm;
+import com.simuwang.shiro.core.ShiroDbRealmImpl;
 import com.simuwang.shiro.core.adapter.LoginAuthAdapter;
 import com.simuwang.shiro.core.bridge.AuthBridgeService;
-import com.simuwang.shiro.core.ShiroDbRealmImpl;
 import com.simuwang.shiro.core.jwt.JwtContext;
 import com.simuwang.shiro.core.jwt.JwtFilter;
 import jakarta.servlet.Filter;
 import org.apache.shiro.authc.credential.CredentialsMatcher;
+import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
+import org.apache.shiro.mgt.DefaultSubjectDAO;
 import org.apache.shiro.spring.LifecycleBeanPostProcessor;
 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.apache.shiro.web.session.mgt.DefaultWebSessionManager;
 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.DependsOn;
 
 import java.util.List;
-import java.util.List;
 import java.util.Map;
 
 @Configuration
@@ -63,15 +62,15 @@ public class ShiroConfig {
         return new AuthBridgeService(this.authAdapter());
     }
 
-    /**
-     * 创建cookie对象
-     */
-    @Bean(name = "sessionIdCookie")
-    public SimpleCookie simpleCookie() {
-        SimpleCookie simpleCookie = new SimpleCookie();
-        simpleCookie.setName("ShiroSession");
-        return simpleCookie;
-    }
+//    /**
+//     * 创建cookie对象
+//     */
+//    @Bean(name = "sessionIdCookie")
+//    public SimpleCookie simpleCookie() {
+//        SimpleCookie simpleCookie = new SimpleCookie();
+//        simpleCookie.setName("ShiroSession");
+//        return simpleCookie;
+//    }
 
     /**
      * 权限管理器
@@ -80,7 +79,13 @@ public class ShiroConfig {
     public DefaultWebSecurityManager defaultWebSecurityManager() {
         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
         securityManager.setRealm(shiroDbRealm());
-        securityManager.setSessionManager(shiroSessionManager());
+//        securityManager.setSessionManager(shiroSessionManager());
+        // 关闭shiro自带的session
+        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
+        DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
+        evaluator.setSessionStorageEnabled(false);
+        subjectDAO.setSessionStorageEvaluator(evaluator);
+        securityManager.setSubjectDAO(subjectDAO);
         return securityManager;
     }
 
@@ -107,18 +112,20 @@ public class ShiroConfig {
         return new ShiroDbRealmImpl(this.authBridgeService(), this.credentialsMatcher());
     }
 
-    /**
-     * 会话管理器
-     */
-    @Bean(name = "sessionManager")
-    public DefaultWebSessionManager shiroSessionManager() {
-        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
-        sessionManager.setSessionValidationSchedulerEnabled(false);
-        sessionManager.setSessionIdCookieEnabled(true);
-        sessionManager.setSessionIdCookie(simpleCookie());
-        sessionManager.setGlobalSessionTimeout(3600000);
-        return sessionManager;
-    }
+//    /**
+//     * 会话管理器
+//     */
+//    @Bean(name = "sessionManager")
+//    public DefaultWebSessionManager shiroSessionManager() {
+//        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
+////        sessionManager.setSessionValidationSchedulerEnabled(false);
+////        sessionManager.setSessionIdCookieEnabled(true);
+////        sessionManager.setSessionIdCookie(simpleCookie());
+////        sessionManager.setGlobalSessionTimeout(3600000);
+//        // 关闭session校验轮训
+//        sessionManager.setSessionValidationSchedulerEnabled(false);
+//        return sessionManager;
+//    }
 
     /**
      * AOP式方法级权限检查
@@ -150,7 +157,7 @@ public class ShiroConfig {
         shiroFilter.setSecurityManager(this.defaultWebSecurityManager());
         // 新增过滤器
         Map<String, Filter> filterMap = MapUtil.newHashMap(true);
-        filterMap.put("jwt", new JwtFilter(this.jwtContext));
+        filterMap.put("jwt", new JwtFilter(this.jwtContext, this.authAdapter()));
         shiroFilter.setFilters(filterMap);
         // 过滤器链配置
         Map<String, String> filterChainMap = MapUtil.newHashMap(16, true);

+ 3 - 0
service-base/src/main/java/com/simuwang/shiro/core/ShiroDbRealmImpl.java

@@ -2,6 +2,7 @@ package com.simuwang.shiro.core;
 
 import com.simuwang.base.common.exception.APIException;
 import com.simuwang.shiro.core.bridge.AuthBridgeService;
+import com.simuwang.shiro.utils.UserUtils;
 import org.apache.shiro.authc.*;
 import org.apache.shiro.authc.credential.CredentialsMatcher;
 import org.apache.shiro.authz.AuthorizationInfo;
@@ -38,6 +39,8 @@ public class ShiroDbRealmImpl extends ShiroDbRealm {
             throw new APIException("账号已禁用");
         }
         shiroUser.setPermissionCodes(this.authBridgeService.findPermissions(shiroUser.getUserId()));
+        // 设置登录用户信息
+        UserUtils.setPrincipal(shiroUser);
         return new SimpleAuthenticationInfo(shiroUser, shiroUser.getPassword(), getName());
     }
 

+ 0 - 44
service-base/src/main/java/com/simuwang/shiro/core/jwt/JwtContext.java

@@ -2,9 +2,6 @@ package com.simuwang.shiro.core.jwt;
 
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.exceptions.JWTDecodeException;
-import com.auth0.jwt.interfaces.DecodedJWT;
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
 import com.simuwang.base.config.DaqProperties;
@@ -17,14 +14,11 @@ import javax.crypto.SecretKey;
 import java.nio.charset.StandardCharsets;
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
-import java.util.Date;
 import java.util.Map;
 
 @Component
 public class JwtContext {
     public static final String HEADER = "Authorization";
-    //    private static final String SECRET = "qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm12";
-//    private static final long EXPIRE = 60 * 24 * 7;
     private static final Cache<String, Map<String, String>> USER_TOKEN_CACHE = Caffeine.newBuilder().maximumSize(16384L).build();
     private final DaqProperties properties;
 
@@ -32,18 +26,6 @@ public class JwtContext {
         this.properties = properties;
     }
 
-//    public static void main(String[] args) {
-//        JwtContext jwtUtil = new JwtContext();
-//        String token = jwtUtil.generateToken("admin");
-//        System.out.println("token = " + token);
-//
-//        Claims claims = jwtUtil.getClaimsByToken(token);
-//        System.out.println("claims = " + claims);
-//
-//        String username = jwtUtil.getClaimFiled(token, "username");
-//        System.out.println("username = " + username);
-//    }
-
     /**
      * 设置用户token缓存
      *
@@ -118,30 +100,4 @@ public class JwtContext {
                 .parseSignedClaims(token)
                 .getPayload();
     }
-
-    /**
-     * 检查token是否过期
-     *
-     * @return true:过期
-     */
-    public boolean isTokenExpired(Date expiration) {
-        return expiration.before(new Date());
-    }
-
-    /**
-     * 获得token中的自定义信息,一般是获取token的username,无需secret解密也能获得
-     *
-     * @param token
-     * @param filed
-     * @return
-     */
-    public String getClaimFiled(String token, String filed) {
-        try {
-            DecodedJWT jwt = JWT.decode(token);
-            return jwt.getClaim(filed).asString();
-        } catch (JWTDecodeException e) {
-//            log.error("JwtContext getClaimFiled error: ", e);
-            return null;
-        }
-    }
 }

+ 9 - 1
service-base/src/main/java/com/simuwang/shiro/core/jwt/JwtFilter.java

@@ -3,6 +3,9 @@ package com.simuwang.shiro.core.jwt;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.simuwang.base.common.util.ServletUtils;
+import com.simuwang.shiro.core.ShiroUser;
+import com.simuwang.shiro.core.adapter.LoginAuthAdapter;
+import com.simuwang.shiro.utils.UserUtils;
 import com.smppw.common.pojo.ResultVo;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.ExpiredJwtException;
@@ -28,9 +31,11 @@ public class JwtFilter extends AccessControlFilter {
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
     private final JwtContext jwtContext;
+    private final LoginAuthAdapter loginAuthAdapter;
 
-    public JwtFilter(JwtContext jwtContext) {
+    public JwtFilter(JwtContext jwtContext, LoginAuthAdapter loginAuthAdapter) {
         this.jwtContext = jwtContext;
+        this.loginAuthAdapter = loginAuthAdapter;
     }
 
     /**
@@ -71,6 +76,9 @@ public class JwtFilter extends AccessControlFilter {
             this.onLoginFail(servletResponse, requestURI, "token非法");
             return false;
         }
+        // 设置登录用户信息
+        ShiroUser shiroUser = this.loginAuthAdapter.findUserByUsername(username);
+        UserUtils.setPrincipal(shiroUser);
         return true;
     }
 

+ 25 - 14
service-base/src/main/java/com/simuwang/shiro/utils/UserUtils.java

@@ -1,9 +1,10 @@
 package com.simuwang.shiro.utils;
 
 import com.simuwang.shiro.core.ShiroUser;
-import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.subject.Subject;
+
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @author wangzaijun
@@ -11,27 +12,37 @@ import org.apache.shiro.subject.Subject;
  * @description 登录用户工具
  */
 public class UserUtils {
-    public static Subject getSubject() {
-        return SecurityUtils.getSubject();
+    private static final ThreadLocal<Map<String, Object>> TOKEN_PRINCIPAL_THREAD = ThreadLocal.withInitial(HashMap::new);
+
+    public static Object getPrincipal() {
+        return TOKEN_PRINCIPAL_THREAD.get().get("principal");
     }
 
-    /**
-     * 获取当前登录的用户信息
-     *
-     * @return 当前登录用户
-     */
-    public static ShiroUser getLoginUser() {
-        Subject subject = SecurityUtils.getSubject();
-        return getLoginUser(subject);
+    public static void setPrincipal(Object principal) {
+        TOKEN_PRINCIPAL_THREAD.get().put("principal", principal);
     }
 
+//    public static Subject getSubject() {
+//        return SecurityUtils.getSubject();
+//    }
+
+//    /**
+//     * 获取当前登录的用户信息
+//     *
+//     * @return 当前登录用户
+//     */
+//    public static ShiroUser getLoginUser() {
+//        Subject subject = SecurityUtils.getSubject();
+//        return getLoginUser(subject);
+//    }
+
     /**
      * 获取当前登录的用户信息
      *
      * @return 当前登录用户
      */
-    public static ShiroUser getLoginUser(Subject subject) {
-        Object principal = subject.getPrincipal();
+    public static ShiroUser getLoginUser() {
+        Object principal = getPrincipal();
         if (principal instanceof ShiroUser shiroUser) {
             return shiroUser;
         }

+ 2 - 2
service-deploy/src/main/resources/application.yml

@@ -96,7 +96,7 @@ simuwang:
     - path: "/v1/rsa-key"
       filters: anon
     - path: "/v1/**"
-      filters: anon
+      filters: jwt
     - path: "/**"
-      filters: anon
+      filters: jwt
 

+ 4 - 3
service-manage/src/main/java/com/simuwang/manage/api/LoginController.java

@@ -14,6 +14,7 @@ import com.simuwang.shiro.utils.UserUtils;
 import com.smppw.common.pojo.ResultVo;
 import com.smppw.common.pojo.enums.status.ResultCode;
 import jakarta.validation.Valid;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.subject.Subject;
 import org.springframework.web.bind.annotation.*;
 
@@ -56,7 +57,7 @@ public class LoginController {
     @PostMapping("login")
     public ResultVo<String> login(@RequestBody LoginUser loginUser) {
         ShiroToken shiroToken = new ShiroToken(loginUser.getUsername(), loginUser.getPassword());
-        Subject subject = UserUtils.getSubject();
+        Subject subject = SecurityUtils.getSubject();
         subject.login(shiroToken);
 
         String requestIp = ServletUtils.getIpAddr();
@@ -73,8 +74,8 @@ public class LoginController {
     @SystemLog(value = "登出", type = SystemLog.Type.LOGOUT)
     @PostMapping("/logout")
     public ResultVo<Boolean> logout() {
-        Subject subject = UserUtils.getSubject();
-        ShiroUser shiroUser = UserUtils.getLoginUser(subject);
+        Subject subject = SecurityUtils.getSubject();
+        ShiroUser shiroUser = UserUtils.getLoginUser();
         String requestIp = ServletUtils.getIpAddr();
         this.jwtContext.cleanUserCache(shiroUser.getUsername(), requestIp);
         subject.logout();