shiro不同角色不同realm验证遇到的坑(getRealms为null)

本文分享了在使用Shiro框架进行多Realm配置时遇到的问题及解决方案。博主最初按照错误的方式配置,导致无法正常获取Realm。经过调整,采用正确的配置方式后成功解决问题,并附上了修改前后的对比代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

博主配置多realm参考的是以下链接

参考:https://blog.youkuaiyun.com/xiangwanpeng/article/details/54802509

代码是没有问题的,但是每次走这步的时候都会返回空值

我的shiro配置文件是写在ini里边的

如图:

 

这样写配置是错误的

 

改成如下写法就能正常获取到realmsl了

 

 

原因:就像装水需要先有容器是一样的道理,配置realm需要先配置realm的管理器

很是MMP。。。。。

 

参考:https://blog.youkuaiyun.com/qq_35981283/article/details/78632312

<think>我们需要在同一个系统中支持两种不同的用户表进行登录认证,且不改变原有的登录方式。我们可以通过自定义Realm来实现多数据源的认证。 在Apache Shiro中,我们可以通过多个Realm来支持多个不同的用户数据源。然后使用ModularRealmAuthenticator来协调多个Realm进行认证。 同时,由于我们使用JWT,我们需要考虑如何生成和验证Token。 步骤: 1. 创建两个Realm,分别用于两种用户表的认证。 2. 配置Shiro,使其支持多个Realm,并设置认证策略(例如,任何一个Realm认证成功即可,或者所有Realm都必须成功)。 3. 由于我们使用JWT,我们需要自定义一个Token类,用于区分不同的登录方式(比如在Token中携带登录类型)。 4. 自定义一个Authenticator,根据Token中的信息选择使用哪个Realm进行认证(或者使用多个Realm尝试认证)。 5. 在登录控制器中,根据不同的登录请求(比如不同的登录接口)创建不同类型的Token。 具体实现: 第一步:定义两种Token,用于区分不同的登录方式(或者可以在同一个Token中携带一个类型字段) 我们可以扩展UsernamePasswordToken,或者使用JWT token中携带一个自定义的字段(如loginType)来区分。 但是,由于我们使用JWT,我们可能会在登录成功后生成JWT token。在登录时,我们可以通过不同的接口来区分不同的用户表,然后在生成Token时记录用户类型(或者使用不同的subject)。 这里我们采用的方式: - 用户登录时,通过不同的登录接口(比如/user/login 和 /admin/login)来区分不同的用户表。 - 在登录控制器中,根据不同的登录接口创建不同的Token(我们可以在Token中设置一个realmName属性,这样Shiro的ModularRealmAuthenticator就可以根据这个属性来选择对应的Realm)。 第二步:创建两个Realm,分别用于认证两种用户。 每个Realm都需要实现doGetAuthenticationInfo方法,从数据库中查询对应的用户信息并返回AuthenticationInfo。 第三步:配置Shiro,将两个Realm都加入SecurityManager,并设置认证策略(这里我们使用AtLeastOneSuccessfulStrategy,即任意一个Realm认证成功即可,但实际中我们可能需要精确指定,所以最好在Token中指定使用哪个Realm,然后只让对应的Realm进行认证)。 更优的方案:自定义一个Authenticator,根据Token中的realmName来调用对应的Realm。 具体步骤: 1. 自定义Token,继承自JWT的Token(或者Shiro的AuthenticationToken),这里我们假设使用JwtToken,但登录时我们使用的是UsernamePasswordToken,所以我们可以自定义一个带登录类型的UsernamePasswordToken。 我们可以这样设计: - 创建`UserTypeUsernamePasswordToken`,包含username、password、userType(或者realmName)。 2. 在登录接口中,根据不同的登录方式创建不同的Token: - 接口1:创建`UserTypeUsernamePasswordToken("username", "password", "type1")` - 接口2:创建`UserTypeUsernamePasswordToken("username", "password", "type2")` 3. 自定义一个`UserTypeModularRealmAuthenticator`,重写`doAuthenticate`方法,根据Token中的userType来选择对应的Realm。 4. 配置Shiro使用这个自定义的Authenticator。 5. 定义两个Realm,分别处理两种类型的用户认证。每个Realm需要有一个唯一的name(比如"realm1"和"realm2"),然后在Token中指定realmName为这个name。 代码示例: 第一步:自定义Token ```java public class UserTypeUsernamePasswordToken extends UsernamePasswordToken { private String realmName; public UserTypeUsernamePasswordToken(String username, String password, String realmName) { super(username, password); this.realmName = realmName; } public String getRealmName() { return realmName; } public void setRealmName(String realmName) { this.realmName = realmName; } } ``` 第二步:自定义Authenticator ```java public class UserTypeModularRealmAuthenticator extends ModularRealmAuthenticator { @Override protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); UserTypeUsernamePasswordToken token = (UserTypeUsernamePasswordToken) authenticationToken; String realmName = token.getRealmName(); if (realmName != null) { Realm realm = getRealm(realmName); if (realm == null) { throw new AuthenticationException("No Realm configured for realm name: " + realmName); } return doSingleRealmAuthentication(realm, token); } else { return doMultiRealmAuthentication(getRealms(), token); } } private Realm getRealm(String realmName) { Collection<Realm> realms = getRealms(); for (Realm realm : realms) { if (realm.getName().equals(realmName)) { return realm; } } return null; } } ``` 第三步:定义两个Realm,例如: ```java public class UserRealm1 extends AuthorizingRealm { // 设置支持的Token类型 @Override public boolean supports(AuthenticationToken token) { return token instanceof UserTypeUsernamePasswordToken; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 授权逻辑 return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UserTypeUsernamePasswordToken upToken = (UserTypeUsernamePasswordToken) token; String username = upToken.getUsername(); // 根据用户名从第一个用户表查询用户 // 这里模拟:假设从数据库查询,如果用户不存在返回null,存在则返回AuthenticationInfo // 实际中需要访问数据库 User user = userService1.findByUsername(username); if (user == null) { return null; } return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); } } public class UserRealm2 extends AuthorizingRealm { // 类似UserRealm1,但是访问的是另一个用户表 @Override public boolean supports(AuthenticationToken token) { return token instanceof UserTypeUsernamePasswordToken; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UserTypeUsernamePasswordToken upToken = (UserTypeUsernamePasswordToken) token; String username = upToken.getUsername(); // 从第二个用户表查询 User2 user = userService2.findByUsername(username); if (user == null) { return null; } return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); } } ``` 注意:在Realm中,我们只处理对应类型的Token。但是在上面的自定义Authenticator中,我们已经根据Token中的realmName选择了对应的Realm,所以每个Realm只需要处理自己的认证逻辑即可。 第四步:配置Shiro(使用Java配置) ```java @Configuration public class ShiroConfig { @Bean public Realm realm1() { UserRealm1 realm = new UserRealm1(); realm.setName("realm1"); // 设置Realm的名称,与Token中的realmName对应 // 设置凭证匹配器,例如使用BCrypt // realm.setCredentialsMatcher(hashedCredentialsMatcher()); return realm; } @Bean public Realm realm2() { UserRealm2 realm = new UserRealm2(); realm.setName("realm2"); return realm; } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置自定义的Authenticator securityManager.setAuthenticator(new UserTypeModularRealmAuthenticator()); // 设置多个Realm securityManager.setRealms(Arrays.asList(realm1(), realm2())); // 其他配置... return securityManager; } // 其他配置如ShiroFilterFactoryBean等 } ``` 第五步:在登录控制器中,创建Token时指定realmName ```java @RestController public class LoginController { @PostMapping("/user/login") public ResponseBean login1(@RequestBody LoginForm form) { // 创建Token,指定realmName为realm1 UserTypeUsernamePasswordToken token = new UserTypeUsernamePasswordToken(form.getUsername(), form.getPassword(), "realm1"); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); // 登录成功,生成JWT token String jwtToken = JwtUtil.generateToken(form.getUsername()); return ResponseBean.success(jwtToken); } catch (AuthenticationException e) { return ResponseBean.error("登录失败"); } } @PostMapping("/admin/login") public ResponseBean login2(@RequestBody LoginForm form) { // 创建Token,指定realmName为realm2 UserTypeUsernamePasswordToken token = new UserTypeUsernamePasswordToken(form.getUsername(), form.getPassword(), "realm2"); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); String jwtToken = JwtUtil.generateToken(form.getUsername()); return ResponseBean.success(jwtToken); } catch (AuthenticationException e) { return ResponseBean.error("登录失败"); } } } ``` 注意:在生成JWT token时,我们可能需要将用户类型(或realmName)也放入JWT的claims中,以便在后续的请求中验证时知道该用户属于哪个Realm(用于授权)。但是,在认证阶段,我们只需要登录成功即可。在授权阶段,我们可能需要根据用户类型来加载不同的权限。 另外,在JWT验证时,我们通常使用一个Realm验证JWT token(因为JWT是无状态的)。所以,我们还需要一个JWT Realm来处理JWT token的认证。这样,我们的系统就同时支持两种登录方式(用户名密码和JWT),并且用户名密码又分为两种用户表。 因此,我们需要再添加一个Realm用于JWT的认证: ```java public class JwtRealm extends AuthorizingRealm { @Override public boolean supports(AuthenticationToken token) { return token instanceof JwtToken; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 根据JWT token中的信息(如用户名和用户类型)来获取授权信息 String username = JwtUtil.getUsername(principals.toString()); // 如何知道用户类型?可以在生成JWT token时加入用户类型,然后在解析后获取 // 这里假设我们在生成JWT token时,将用户类型作为claim放入 // 在JwtUtil中解析token时,可以获取到用户类型 // 然后根据用户类型去不同的服务获取权限 // 但是注意:在JwtRealm中,我们可能需要同时处理两种用户类型的授权,所以需要区分 // 可以在生成JWT token时,在subject或者claims中加入用户类型标识 // 例如:生成token时:JwtUtil.generateToken(username, userType) // 解析时,可以获取到userType,然后根据userType选择不同的服务获取权限 return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String jwtToken = (String) token.getCredentials(); // 验证jwtToken的合法性 if (!JwtUtil.verifyToken(jwtToken)) { throw new AuthenticationException("Invalid token"); } // 从JWT token中解析出用户名 String username = JwtUtil.getUsername(jwtToken); // 这里不需要密码,因为JWT已经自包含验证信息,所以返回一个SimpleAuthenticationInfo,密码部分可以放jwtToken本身 return new SimpleAuthenticationInfo(jwtToken, jwtToken, getName()); } } ``` 这样,我们的Shiro配置中需要加入三个Realmrealm1、realm2和jwtRealm。同时,在自定义的Authenticator中,我们只处理了UserTypeUsernamePasswordToken,而JwtToken将由JwtRealm处理(因为JwtRealm的supports方法返回true)。所以,我们需要修改自定义Authenticator,使其能够处理多种Token类型,或者使用默认的ModularRealmAuthenticator(它本身支持多个Realm,并且根据Realm的supports方法选择Realm)。但是,我们之前自定义的UserTypeModularRealmAuthenticator只处理了UserTypeUsernamePasswordToken,所以我们需要调整。 调整方案: - 我们可以不使用自定义的Authenticator,而是使用默认的ModularRealmAuthenticator,并设置认证策略(例如FirstSuccessfulStrategy)。然后,在Realm中,通过supports方法来判断是否支持当前Token。这样,对于UserTypeUsernamePasswordToken,两个Realm都会支持(因为我们在两个Realm的supports方法中都返回true),然后两个Realm都会去认证,这显然不是我们想要的。 所以,我们还是需要自定义Authenticator来根据Token中的realmName选择对应的Realm。但是,对于JwtToken,我们不希望走这个选择逻辑,而是希望由JwtRealm来处理。因此,我们可以在自定义Authenticator中,判断Token的类型,如果是JwtToken,则使用JwtRealm;如果是UserTypeUsernamePasswordToken,则根据realmName选择对应的Realm。 修改自定义Authenticator: ```java public class UserTypeModularRealmAuthenticator extends ModularRealmAuthenticator { @Override protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); Collection<Realm> realms = getRealms(); if (authenticationToken instanceof JwtToken) { // 如果是JwtToken,则只使用JwtRealm return doSingleRealmAuthentication(findJwtRealm(realms), authenticationToken); } else if (authenticationToken instanceof UserTypeUsernamePasswordToken) { UserTypeUsernamePasswordToken token = (UserTypeUsernamePasswordToken) authenticationToken; String realmName = token.getRealmName(); if (realmName != null) { Realm realm = findRealmByName(realms, realmName); if (realm == null) { throw new AuthenticationException("No Realm configured for realm name: " + realmName); } return doSingleRealmAuthentication(realm, token); } else { return doMultiRealmAuthentication(realms, token); } } else { return doMultiRealmAuthentication(realms, authenticationToken); } } private Realm findJwtRealm(Collection<Realm> realms) { for (Realm realm : realms) { if (realm instanceof JwtRealm) { return realm; } } throw new AuthenticationException("No JwtRealm configured"); } private Realm findRealmByName(Collection<Realm> realms, String realmName) { for (Realm realm : realms) { if (realm.getName().equals(realmName)) { return realm; } } return null; } } ``` 这样,在Shiro配置中,我们需要将三个Realm都设置进去。 注意:在JwtRealm中,我们不需要指定name,因为我们在选择时是根据类型(JwtRealm)来选择的。但是,为了统一,也可以给JwtRealm设置一个name,然后在Token中指定realmName为这个name?但是JwtToken并没有携带realmName,所以我们通过类型判断。 最后,在ShiroFilter中配置拦截规则,对于登录接口放行,其他接口需要认证。 总结: - 我们通过自定义Token和自定义Authenticator,实现了多用户表的登录认证。 - 同时,我们支持JWT token的认证。 - 在登录接口中,通过创建不同的Token(指定realmName)来选择对应的用户表进行认证。 注意:密码加密匹配问题,两个用户表可能使用不同的加密方式,可以在各自的Realm中设置不同的CredentialsMatcher。 以上方案可以实现需求,且对原有代码改动较小。 但是,如果原有系统已经有一个Realm,那么我们需要重构,将原来的Realm拆分成两个,或者保留原来的Realm,并新增一个Realm,然后修改登录接口,让新的登录接口使用新的Realm。 如果原有系统已经有一个Realm(比如UserRealm1),那么我们需要: - 保留UserRealm1,并修改它,使其支持UserTypeUsernamePasswordToken(在supports方法中判断)。 - 新增UserRealm2,用于第二种用户表。 - 新增JwtRealm,用于JWT认证。 - 配置自定义的Authenticator和多个Realm。 这样,原有的登录接口可能不需要改变(因为UserRealm1仍然存在),新的登录接口使用新的Realm。 如果原有系统没有使用自定义Token,那么我们需要修改登录接口,将原有的Token替换成UserTypeUsernamePasswordToken,并指定realmName(比如"realm1"),这样原有的用户认证仍然通过UserRealm1完成。 因此,这个方案可以平滑扩展。 注意:在授权部分,我们还没有实现。在授权时,我们需要根据用户类型(即来自哪个Realm)去获取不同的权限数据。这可以在Realm的doGetAuthorizationInfo方法中实现,根据Principal(用户名)和用户类型(可以在生成AuthenticationInfo时放入Principal中,例如放入一个自定义的Principal对象,包含用户名和用户类型)来获取权限。 例如: ```java // 在UserRealm1的认证方法中 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // ... 获取用户 // 使用自定义的Principal,包含用户名和用户类型 SimplePrincipalCollection principal = new SimplePrincipalCollection(); principal.add(user.getUsername(), getName()); // 这里将Realm name作为identity的标识,也可以使用自定义对象 // 或者使用自定义的Principal对象 // 例如:UserPrincipal principal = new UserPrincipal(user.getId(), user.getUsername(), "type1"); // 然后:return new SimpleAuthenticationInfo(principal, user.getPassword(), getName()); // 这样在授权时,doGetAuthorizationInfo的PrincipalCollection中就可以获取到这个UserPrincipal对象 return new SimpleAuthenticationInfo(principal, user.getPassword(), getName()); } ``` 在授权时,我们可以从Principal中获取用户类型,然后根据用户类型去不同的服务获取权限。 由于时间关系,这里不再展开授权部分的实现。 以上就是一个完整的扩展方案。 注意:此方案中,JWT token的生成和验证需要能够区分用户类型(在生成token时加入用户类型),以便在授权时使用。 例如,在登录成功后生成token: ```java // 在登录控制器中,登录成功后 // 获取用户类型(根据登录的接口,或者根据Token中的realmName) String userType = token.getRealmName(); // 这里token是UserTypeUsernamePasswordToken,其realmName就是用户类型 String jwtToken = JwtUtil.generateToken(username, userType); ``` 在JwtUtil中,将userType放入claims: ```java public static String generateToken(String username, String userType) { // 设置过期时间 Date now = new Date(); Date expireDate = new Date(now.getTime() + EXPIRE_TIME); return Jwts.builder() .setSubject(username) .claim("userType", userType) // 加入用户类型 .setIssuedAt(now) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); } ``` 这样,在JwtRealm的授权方法中,我们可以从token中解析出userType,然后根据userType选择不同的权限服务。 最后,在ShiroConfig中配置好三个Realm和自定义的Authenticator,以及SecurityManager。 这样,我们就实现了两种用户表的登录,并且支持JWT认证。 注意:在ShiroFilter中,我们需要配置所有请求都需要通过JwtFilter(用于验证JWT token),而登录接口不需要。所以,我们需要一个自定义的Filter来验证JWT token。 自定义JwtFilter: ```java public class JwtFilter extends AuthenticatingFilter { // 这里实现JWT token的验证和登录 @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest httpRequest = (HttpServletRequest) request; String jwt = httpRequest.getHeader("Authorization"); if (jwt != null && jwt.startsWith("Bearer ")) { jwt = jwt.substring(7); } return new JwtToken(jwt); } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { // 尝试登录 try { executeLogin(request, response); return true; } catch (Exception e) { // 跳转到登录页面或者返回错误信息 response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); out.write("未授权"); out.flush(); out.close(); return false; } } } ``` 然后在ShiroConfig中配置Filter: ```java @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); Map<String, Filter> filters = new HashMap<>(); filters.put("jwt", new JwtFilter()); factoryBean.setFilters(filters); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/user/login", "anon"); filterChainDefinitionMap.put("/admin/login", "anon"); filterChainDefinitionMap.put("/**", "jwt"); factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return factoryBean; } ``` 这样,我们就完成了整个配置。 由于问题复杂,代码量较大,以上代码仅供参考,具体实现需要根据项目调整。 最后,请注意密码加密和JWT安全相关的问题。 §§相关问题§§: 1. 在Shiro中,如何实现多个Realm的授权? 2. 如何在不同Realm中使用不同的密码加密方式? 3. 在JWT token中存储用户类型是否安全?如何增强安全性? 4. 如果需要在同一个接口中支持多种用户类型登录(比如根据用户名自动判断属于哪个用户表),应该如何修改?</think>在Spring Boot + Shiro + JWT架构中支持两种用户表的登录认证,同时保留原有登录方式,可以通过以下方案实现: ### 解决方案 1. **创建自定义Token**:区分两种登录方式 2. **实现多Realm认证**:每个Realm处理一种用户表 3. **配置多Realm策略**:使用ModularRealmAuthenticator 4. **自定义匹配器**:根据Token类型选择Realm 5. **登录接口扩展**:新增接口支持第二种登录 ### 核心代码实现 #### 1. 自定义Token(添加登录类型标识) ```java public class MultiUserToken extends UsernamePasswordToken { private String loginType; // 标识登录类型(user1/user2) public MultiUserToken(String username, String password, String loginType) { super(username, password); this.loginType = loginType; } // Getter } ``` #### 2. 实现第二个Realm(处理新用户表) ```java public class User2Realm extends AuthorizingRealm { @Override public boolean supports(AuthenticationToken token) { return token instanceof MultiUserToken && "user2".equals(((MultiUserToken) token).getLoginType()); } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { String username = (String) token.getPrincipal(); // 查询第二个用户表 User2 user = user2Service.findByUsername(username); if (user == null) throw new UnknownAccountException(); return new SimpleAuthenticationInfo( user.getUsername(), user.getEncryptedPassword(), ByteSource.Util.bytes(user.getSalt()), getName() ); } // 授权方法省略... } ``` #### 3. 修改原有Realm(添加类型检查) ```java public class User1Realm extends AuthorizingRealm { @Override public boolean supports(AuthenticationToken token) { return token instanceof MultiUserToken && ("user1".equals(((MultiUserToken) token).getLoginType()) || !(token instanceof MultiUserToken)); // 兼容旧Token } // 原有认证逻辑保持不变... } ``` #### 4. 配置Shiro支持多Realm ```java @Configuration public class ShiroConfig { @Bean public Realm user1Realm() { User1Realm realm = new User1Realm(); realm.setCredentialsMatcher(new HashedCredentialsMatcher("SHA-256")); return realm; } @Bean public Realm user2Realm() { User2Realm realm = new User2Realm(); realm.setCredentialsMatcher(new BCryptCredentialsMatcher()); return realm; } @Bean public Authenticator authenticator() { ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); authenticator.setRealms(Arrays.asList(user1Realm(), user2Realm())); authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy()); return authenticator; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setAuthenticator(authenticator()); return manager; } } ``` #### 5. 新增登录控制器 ```java @RestController public class LoginController { // 原有登录接口(兼容旧客户端) @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest request) { return doLogin(request.getUsername(), request.getPassword(), "user1"); } // 新增登录接口 @PostMapping("/login/user2") public ResponseEntity<?> loginUser2(@RequestBody LoginRequest request) { return doLogin(request.getUsername(), request.getPassword(), "user2"); } private ResponseEntity<?> doLogin(String username, String password, String type) { try { Subject subject = SecurityUtils.getSubject(); subject.login(new MultiUserToken(username, password, type)); // 生成JWT(需包含用户类型) String jwt = JwtUtil.generateToken(username, type); return ResponseEntity.ok(new LoginResponse(jwt)); } catch (AuthenticationException e) { return ResponseEntity.status(401).body("认证失败"); } } } ``` #### 6. JWT工具类增强(携带用户类型) ```java public class JwtUtil { public static String generateToken(String username, String userType) { return Jwts.builder() .setSubject(username) .claim("userType", userType) // 添加用户类型声明 .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); } public static String getUserType(String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); return claims.get("userType", String.class); } } ``` ### 方案说明 1. **兼容性设计**: - 旧登录接口继续使用`/login`路径 - 新增`/login/user2`接口处理第二种用户 - 原有Realm自动兼容旧Token格式 2. **认证流程**: ```mermaid sequenceDiagram 客户端->>登录接口: 提交凭证(含loginType) 登录接口->>Shiro: 提交MultiUserToken Shiro->>User1Realm: 检查是否支持Token User1Realm-->>Shiro: 不支持(user2) Shiro->>User2Realm: 检查是否支持Token User2Realm->>User2Realm: 执行认证逻辑 User2Realm-->>Shiro: 返回认证结果 Shiro-->>登录接口: 返回认证状态 登录接口->>客户端: 返回JWT(含userType) ``` 3. **关键优势**: - 零修改原有用户表认证逻辑 - 清晰分离两种用户体系 - JWT携带用户类型信息 - 支持不同的加密策略(如示例中的SHA256和BCrypt) 4. **授权扩展**: - 在`doGetAuthorizationInfo`中根据JWT中的userType加载不同权限 - 自定义注解控制不同用户类型的访问权限 ### 注意事项 1. Realm的`supports()`方法必须正确实现类型检查 2. JWT中必须包含用户类型信息 3. 不同用户表建议使用不同密码加密方式 4. 授权时需根据用户类型加载对应权限 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值