權限管理:shiro

 

圖一: 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章