权限管理:shiro

2022-08-15 10:06:47

图一: shiro整体架构功能

图二: shiro架构API实现方式

@DependsOn(value="springUtils")
@Configuration
public class ShiroCoreConfig {

	@Autowired
	private RedisProperties redisParam;
	
	public static final String CACHE_KEY_PREFIX = "cache:key:";
	public static final String SESSION_KEY_PREFIX = "session:key:";
	
	public static final int SESSION_EXPIRE = 60*60;
	public static final int CACHE_EXPIRE = 60*60;
	
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager);
		return advisor;
	}
	
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        
        shiroFilterFactoryBean.setSecurityManager(securityManager);
      
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/api/s/sys/login", "anon");
        filterChainDefinitionMap.put("/api/s/sys/logout", "anon");
        filterChainDefinitionMap.put("/api/sys/**", "authc");
        
        shiroFilterFactoryBean.setLoginUrl("/api/s/sys/unauth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }
	
    @Bean
	public SecurityManager securityManager(CoreRealm coreRealm,
			RedisCacheManager redisCacheManager,
			SessionManager sessionManager,
			CookieRememberMeManager rememberMeManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setCacheManager(redisCacheManager);
		securityManager.setRealm(coreRealm);
		securityManager.setSessionManager(sessionManager);
		securityManager.setRememberMeManager(rememberMeManager);
		return securityManager;
	}
    
	@Bean
	public SimpleCookie simpleCookie() {
		SimpleCookie simpleCookie = new SimpleCookie("RememberMe_Cookie");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setMaxAge(60*60*24*30);
		return simpleCookie;
	}
	
	@Bean
	public CookieRememberMeManager rememberMeManager(SimpleCookie simpleCookie) {
		CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
		rememberMeManager.setCookie(simpleCookie);
		//cipherKey是加密rememberMe Cookie的密钥
		rememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
		return rememberMeManager;
	}
	
	@Bean
	public HashedCredentialsMatcher credentialsMatcher() {
		HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
		credentialsMatcher.setHashAlgorithmName(SHA256Util.HASH_ALGORITHM_NAME);
		credentialsMatcher.setHashIterations(SHA256Util.HASH_ITERATIONS);
		return credentialsMatcher;
	}
	
	@Bean
	public CoreRealm coreRealm(HashedCredentialsMatcher credentialsMatcher) {
		CoreRealm realm = new CoreRealm();
		realm.setCredentialsMatcher(credentialsMatcher);
		return realm;
	}
	
	@Bean
	public RedisManager redisManager() {
		RedisManager redisManager = new RedisManager();
		redisManager.setHost(redisParam.getHost()+":"+redisParam.getPort());
		redisManager.setPassword(redisParam.getPassword());
		redisManager.setTimeout(redisParam.getTimeout());
		redisManager.setDatabase(redisParam.getDatabase());
		return redisManager;
	}
	
	@Bean
	public RedisSessionIdGenerator redisSessionIdGenerator() {
		return new RedisSessionIdGenerator();
	}
	
	@Bean
	public RedisSessionDAO redisSessionDAO(RedisManager redisManager,RedisSessionIdGenerator redisSessionIdGenerator) {
		RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
		redisSessionDAO.setSessionIdGenerator(redisSessionIdGenerator);
		redisSessionDAO.setRedisManager(redisManager);
		redisSessionDAO.setKeyPrefix(SESSION_KEY_PREFIX);
		redisSessionDAO.setExpire(SESSION_EXPIRE);
		return redisSessionDAO;
	}
	
	@Bean
	public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
		RedisSessionManager manager = new RedisSessionManager();
		manager.setSessionDAO(redisSessionDAO);
		return manager;
	}
	
	@Bean
	public RedisCacheManager redisCacheManager(RedisManager redisManager) {
		RedisCacheManager cacheManager = new RedisCacheManager();
		cacheManager.setRedisManager(redisManager);
		cacheManager.setKeyPrefix(CACHE_KEY_PREFIX);
		cacheManager.setExpire(CACHE_EXPIRE);
		cacheManager.setPrincipalIdFieldName("username");
		return cacheManager;
	}

}

核心代码: 配置SecurityManager(囊括所有核心模块)

public class CoreRealm extends AuthorizingRealm {

	private ManagerService service = SpringUtils.getBean(ManagerService.class);
	
	private SysRoleRep roleRep = SpringUtils.getBean(SysRoleRep.class);
	
	private SysPermissionRep permissionRep = SpringUtils.getBean(SysPermissionRep.class);
	
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
	
		log.info("Authorization......");
		
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		
		String principal = (String) principals.getPrimaryPrincipal();
		
		List<SysRole> roles = roleRep.rolesByUsername(principal);
		
		List<String> roleList = roles.stream().map(SysRole::getRoleTargetId).collect(Collectors.toList());
		
		List<String> permissionList = permissionRep
										.permissionsByUsername(roles.stream().map(SysRole::getId).collect(Collectors.toList()))
										.stream().map(SysPermission::getPermissionTargetId).collect(Collectors.toList());
			
		authorizationInfo.setRoles(new HashSet<String>(roleList));
		authorizationInfo.setStringPermissions(new HashSet<String>(permissionList));
		
		log.info("---------------------------------------");
		log.info("授权用户信息:{}",principal);
		log.info("授权角色列表:{}",roleList);
		log.info("授权权限列表:{}",permissionList);
		log.info("---------------------------------------");
		
		return authorizationInfo;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		/*
		 * 业务逻辑:根据用户名查看用户信息
		 */
		SysUser sysUser = service.getUserByUsername(token.getPrincipal().toString());
		
		if(sysUser == null || StringUtils.isBlank(sysUser.getId())) {
			throw new AuthenticationException("用户名或密码错误!");
		}
		
		switch (sysUser.getState()) {
		case 1:
			throw new LockedAccountException("账号已被锁定,请到后台处理!"); 
		case 2:
			throw new DisabledAccountException("账号已被禁用,请规范登录!");  
		default:
			break;
		}
		
		String hashedCredentials = sysUser.getPassword();
		
		ByteSource credentialsSalt = ByteSource.Util.bytes(sysUser.getUsername());
		
		SimpleAuthenticationInfo authenticationInfo = 
				new SimpleAuthenticationInfo(token.getPrincipal(), hashedCredentials, credentialsSalt, getName());
		
		// 单点登录 : 删除之前登录人信息。(这里的单点登录不是多系统的单点登录)
		ShiroUtils.deleteCache(token.getPrincipal().toString(), true);
		
		return authenticationInfo;
	}
}

Realm使用样例

//SessionId生长成器
public class RedisSessionIdGenerator implements SessionIdGenerator {

	@Override
	public Serializable generateId(Session session) {
		String uuid = UUID.randomUUID().toString().replaceAll("-", "");
		return String.format(uuid);
	}

}

//RedisSessionManager
public class RedisSessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "Authorization";
    
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
	
	public RedisSessionManager() {
		 //重写构造器
        super();
        this.setDeleteInvalidSessions(true);
	}

	@Override
	protected Serializable getSessionId(ServletRequest request,ServletResponse response) {
		String token = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
		// 如果请求头中存在token 则从请求头中获取token
		if (!StringUtils.isEmpty(token)) {
			request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
			request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token);
			request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
			return token;
		} else {
			// 否则按默认规则从cookie取token
			return super.getSessionId(request, response);
		}
	}

}
        //开源shiro-redis支持
        <!-- Shiro-redis插件 -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.2.3</version>
        </dependency>

SessionManager之RedisSessionManager。

@Slf4j
public class ShiroUtils {

	private static RedisSessionDAO redisSessionDAO = SpringUtils.getBean(RedisSessionDAO.class);
	
	/*
	 * 获取session
	 */
	public static Session getSession() {
		return SecurityUtils.getSubject().getSession();
	}
	
//	/*
//	 * 获取当前用户角色信息
//	 */
//	public static SysRole getRole() {
//		SysUserMapper userMapper = (SysUserMapper) SpringUtils.getBean(SysUserMapper.class);
//		return userMapper.findRolesByUserId(getUser().getUserId()); 
//	}
//	
//	/*
//	 * 获取用户信息
//	 */
//	public static SysUser getUser() {
//		return (SysUser)SecurityUtils.getSubject().getPrincipal();
//	}
	
	/*
	 * 登出
	 */
	public static void logout() {
		SecurityUtils.getSubject().logout();
	}
	
	/*
	 * 删除用户缓存信息
	 */
	public static void deleteCache(String username,boolean isRemoveSession) {
       
		Session session = null;
        
        Collection<Session> sessions = redisSessionDAO.getActiveSessions();
        
        String redisName = ""; 
        
        Object attribute = null;
        
        for(Session sessionInfo : sessions){
            //遍历Session,找到该用户名称对应的Session
        	log.info("redis中Session中的参数:{}",DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
          
        	attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
           
        	if (attribute == null) {
                continue;
            }
           
            log.info("redis中Session中参数的值:{}",attribute);
           
            redisName = (String) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if (redisName == null) {
                continue;
            }
            
            if (Objects.equals(redisName, username)) {
                session=sessionInfo;
            }
        }
        
        if (session == null||attribute == null) {
            return;
        }
        
        //删除session
        if (isRemoveSession) {
        	log.info("删除session:{}",session);
            redisSessionDAO.delete(session);
        }
        
        //删除Cache,在访问受限接口时会重新授权
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        
        Authenticator authc = securityManager.getAuthenticator();
        
        ((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute);
	}
}

ShiroUtils:工具类(相关用户信息、角色,根据使用的持久化框架实现)

/*
SpringUtils: 利用单个bean的装载顺序,实现ApplicationContextAware注入,如果涉及
依赖注入多个bean,使用@DependsOn先行注入依赖Bean。(这里实现BeanPostProcessor的
目的也是想提前装载SpringUtils,但是测试时发现注入还是不够靠前,相对于不实现
BeanPostProcessor是有提前的。)
*/

@Component
public class SpringUtils implements ApplicationContextAware,BeanPostProcessor{

	private static ApplicationContext ac;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		ac = applicationContext;
	}

	public static void cheakApplicationContext(){
		if(ac==null)
			throw new IllegalStateException("ApplicationContext is Emtey");
	}
	
	public static void clearApplicatinContext(){
		ac = null;
	}
	
	public static <T> T getBean(Class<T> cla){
		cheakApplicationContext();
		return ac.getBean(cla);
	}

	public static <T> T getBean(String beanName){
		cheakApplicationContext();
		return (T) ac.getBean(beanName);
	}
}
public class SHA256Util {

	//加密名称
	public static final String HASH_ALGORITHM_NAME = "SHA-256";
	//加密
	public static final int HASH_ITERATIONS = 15;
	
	public static String sha256(String password,String salt) {
		return new SimpleHash(HASH_ALGORITHM_NAME, password, salt, HASH_ITERATIONS).toString();
	}
	
	public static void main(String[] args) {
		String sha256 = sha256("123456", "admin");
		System.out.println(sha256+"\n"+sha256.length());
	}
}

两个工具类。

小编这里,主要是上手快速使用。(略有不足,欢迎小伙伴留言)

参考博主: SpringBoot整合Shiro

  • 作者:黑狗子
  • 原文链接:https://blog.csdn.net/weixin_38289303/article/details/104922981
    更新时间:2022-08-15 10:06:47