package com.usky.config.shiro; import com.usky.config.shiro.jwt.JwtToken; import com.usky.constant.Constant; import com.usky.entity.sys.vo.SysUserVO; import com.usky.service.sys.menuService.MenuService; import com.usky.service.sys.user.UserService; import com.usky.utils.AuthorizationUtils; import com.usky.utils.RedisUtil; import com.usky.utils.jwt.JwtUtil; import com.usky.utils.jwt.common.StringUtil; import lombok.extern.slf4j.Slf4j; 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.apache.shiro.subject.SimplePrincipalCollection; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Set; @Service @Slf4j public class UserRealm extends AuthorizingRealm { @Resource @Lazy private UserService userService; @Resource @Lazy private MenuService menuService; @Resource @Lazy private RedisUtil redisUtil; @Override public boolean supports(AuthenticationToken authenticationToken) { return authenticationToken instanceof JwtToken; } /** * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); String loginName = JwtUtil.getClaim(principalCollection.toString(), Constant.ACCOUNT); if (StringUtil.isBlank(loginName)) { throw new AuthenticationException("Token中帐号为空"); } SysUserVO sysUser = userService.queryuserByLoginName(loginName); if (sysUser != null) { // 角色列表 Set roles; // 功能列表 Set menus; log.info("===============Shiro权限认证开始============ [ roles、permissions]=========="); if (sysUser.isAdmin()) { simpleAuthorizationInfo.addRole("admin"); simpleAuthorizationInfo.addStringPermission("*:*:*"); } else { roles = userService.queryRoleKeys(sysUser.getUserId()); menus = menuService.qyeryPermsByUserId(sysUser.getUserId()); // 角色加入AuthorizationInfo认证对象 simpleAuthorizationInfo.setRoles(roles); // 权限加入AuthorizationInfo认证对象 simpleAuthorizationInfo.setStringPermissions(menus); } log.info("===============Shiro权限认证成功=============="); } return simpleAuthorizationInfo; } /** * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String token = (String) authenticationToken.getCredentials(); // 解密获得account,用于和数据库进行对比 String loginName = JwtUtil.getClaim(token, "loginName"); // 帐号为空 if (StringUtil.isBlank(loginName)) { throw new AuthenticationException("Token中帐号为空"); } // 查询用户是否存在 SysUserVO sysUser = userService.queryuserByLoginName(loginName); if (sysUser == null) { throw new AuthenticationException("该帐号不存在"); } // 开始认证,要AccessToken认证通过,且Redis中存在RefreshToken,且两个Token时间戳一致 if (JwtUtil.verify(token) && redisUtil.hasKey(Constant.PREFIX_SHIRO_REFRESH_TOKEN + loginName)) { // 获取RefreshToken的时间戳 String currentTimeMillisRedis = redisUtil.get(Constant.PREFIX_SHIRO_REFRESH_TOKEN + loginName).toString(); // 获取AccessToken时间戳,与RefreshToken的时间戳对比 if (JwtUtil.getClaim(token, Constant.CURRENT_TIME_MILLIS).equals(currentTimeMillisRedis)) { return new SimpleAuthenticationInfo(token, token, "userRealm"); } } throw new AuthenticationException("Token已过期"); } /** * 清除当前用户的权限认证缓存 * * @param principals 权限信息 */ @Override public void clearCache(PrincipalCollection principals) { super.clearCache(principals); } /** * 清理所有用户授权信息缓存 */ public void clearAllCachedAuthorizationInfo() { AuthorizationUtils.clearAllCachedAuthorizationInfo(); } }