spring boot整合shiro 簡單許可權控制
package me.config;
import javax.annotation.Resource;
import me.domain.entity.CmsUser;
import me.service.UserService;
import me.utils.MD5Util;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.support.SimpleCacheManager;
import java.util.List;
import java.util.Map;
/**
* 身份校驗核心類;
* @author gjy
* @version v.1.0
*/
public class MyShiroRealm extends AuthorizingRealm{
// @Override
// public boolean supports(AuthenticationToken token) {
//// return super.supports(token);
// ////僅支援UsernamePasswordToken 型別的Token
// return token instanceof UsernamePasswordToken;
// }
@Resource private UserService userService; /** * 認證資訊.(身份驗證) * : * Authentication 是用來驗證使用者身份 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("MyShiroRealm.doGetAuthenticationInfo()"); //加這一步的目的是在Post請求的時候會先進認證,然後在到請求 if (token.getPrincipal() == null) { return null; } //獲取使用者的輸入的賬號. String username = (String) token.getPrincipal(); //通過username從資料庫中查詢 User物件,如果找到,沒找到. //實際專案中,這裡可以根據實際情況做快取,如果不做,Shiro自己也是有時間間隔機制,2分鐘內不會重複執行該方法 CmsUser cmsUser = userService.getCmsUserByUsername(username); //加密方式; //交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( username, //使用者名稱 MD5Util.getMD5(cmsUser.getPasswd()), //密碼 // ByteSource.Util.bytes(lists.get(0).get("password").toString()),//salt=username+salt getName() //realm name ); return authenticationInfo; } /** * 此方法呼叫 hasRole,hasPermission的時候才會進行回撥. * * 許可權資訊.(授權): * 1、如果使用者正常退出,快取自動清空; * 2、如果使用者非正常退出,快取自動清空; * 3、如果我們修改了使用者的許可權,而使用者不退出系統,修改的許可權無法立即生效。 * (需要手動程式設計進行實現;放在service進行呼叫) * 在許可權修改後呼叫realm中的方法,realm已經由spring管理,所以從spring中獲取realm例項, * 呼叫clearCached方法; * :Authorization 是授權訪問控制,用於對使用者進行的操作授權,證明該使用者是否允許進行當前操作,如訪問某個連結,某個資原始檔等。 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { /* * 當沒有使用快取的時候,不斷重新整理頁面的話,這個程式碼會不斷執行, * 當其實沒有必要每次都重新設定許可權資訊,所以我們需要放到快取中進行管理; * 當放到快取中時,這樣的話,doGetAuthorizationInfo就只會執行一次了, * 快取過期之後會再次執行。 */ System.out.println("許可權配置-->MyShiroRealm.doGetAuthorizationInfo()"); //獲取登入使用者名稱 String username= (String) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); //實際專案中,這裡可以根據實際情況做快取,如果不做,Shiro自己也是有時間間隔機制,2分鐘內不會重複執行該方法 List<Map<String,Object>> lists = userService.getAuthorizationInfo(username); ///在認證成功之後返回. //設定角色資訊. //支援 Set集合, //使用者的角色對應的所有許可權,如果只使用角色定義訪問許可權,下面的四行可以不要 for(Map<String,Object> map:lists){ authorizationInfo.addRole(map.get("role").toString()); for(Map<String,Object> map1:lists){ authorizationInfo.addStringPermission(map1.get("permission").toString()); } } //authorizationInfo.addRole("admin"); //新增許可權 //authorizationInfo.addStringPermission("admin:manage"); //設定許可權資訊.
// authorizationInfo.setStringPermissions(getStringPermissions());
return authorizationInfo;
}
public void clearCached() {
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
/**
* 將許可權物件中的 許可權code取出.
* @param permissions
* @return
*/
// public Set getStringPermissions(Set permissions){
// Set stringPermissions = new HashSet();
// if(permissions != null){
// for(SysPermission p : permissions) {
// stringPermissions.add(p.getPermission());
// }
// }
// return stringPermissions;
// }
}
package me.config;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Shiro 配置
*
Apache Shiro 核心通過 Filter 來實現,就好像SpringMvc 通過DispachServlet 來主控制一樣。
既然是使用 Filter 一般也就能猜到,是通過URL規則來進行過濾和許可權校驗,所以我們需要定義一系列關於URL的規則和訪問許可權。
*
* @author gjy
* @version v 1.0
*/
@Configuration
public class ShiroConfiguration {
@Value(“${spring.redis.host}”)
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.password}")
private String password;
/**
* ShiroFilterFactoryBean 處理攔截資原始檔問題。
* 注意:單獨一個ShiroFilterFactoryBean配置是或報錯的,以為在
* 初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager
*
Filter Chain定義說明
1、一個URL可以配置多個Filter,使用逗號分隔
2、當設定多個過濾器時,全部驗證通過,才視為通過
3、部分過濾器可指定引數,如perms,roles
*
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設定 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
//攔截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
//配置退出 過濾器,其中的具體的退出程式碼Shiro已經替我們實現了
filterChainDefinitionMap.put("/logout", "logout");
//<!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 -->:這是一個坑呢,一不小心程式碼就不好使了;
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/html/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/fonts/**","anon");
filterChainDefinitionMap.put("/images/**","anon");
filterChainDefinitionMap.put("/lib/**","anon");
filterChainDefinitionMap.put("/plugins/**","anon");
filterChainDefinitionMap.put("/static/**","anon");
filterChainDefinitionMap.put("/home/login","anon");
//<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
filterChainDefinitionMap.put("/**", "authc");
// 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/html/login.html");
// 登入成功後要跳轉的連結
shiroFilterFactoryBean.setSuccessUrl("/html/index.html");
//未授權介面;
shiroFilterFactoryBean.setUnauthorizedUrl("/html/404.html");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){
System.out.println("securityManager---->>>");
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 設定realm.
securityManager.setRealm(myShiroRealm());
// 自定義快取實現 使用redis
securityManager.setCacheManager(cacheManager());
// 自定義session管理 使用redis
securityManager.setSessionManager(sessionManager());
//注入記住我管理器;
// securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
/**
* 配置shiro redisManager
* 使用的是shiro-redis開源外掛
* @return
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setExpire(1800);// 配置快取過期時間
redisManager.setTimeout(timeout);
redisManager.setPassword(password);
return redisManager;
}
/**
* cacheManager 快取 redis實現
* 使用的是shiro-redis開源外掛
* @return
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* RedisSessionDAO shiro sessionDao層的實現 通過redis
* 使用的是shiro-redis開源外掛
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
System.out.println(redisSessionDAO.getKeyPrefix()+"****----");
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* Session Manager
* 使用的是shiro-redis開源外掛
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
/**
* cookie管理物件;記住我功能
* @return
*/
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的金鑰 建議每個專案都不一樣 預設AES演算法 金鑰長度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode(""));
return cookieRememberMeManager;
}
/**
* cookie物件;
* @return
*/
public SimpleCookie rememberMeCookie(){
//這個引數是cookie的名稱,對應前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//<!-- 記住我cookie生效時間30天 ,單位秒;-->
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
/**
* 身份認證realm,賬號密碼校驗;許可權等;
* @return
*/
@Bean
public MyShiroRealm myShiroRealm(){
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());;
return myShiroRealm;
}
/**
* 憑證匹配器
* (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了
* 所以我們需要修改下doGetAuthenticationInfo中的程式碼;
* )
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//雜湊演算法:這裡使用MD5演算法;
hashedCredentialsMatcher.setHashIterations(1);//雜湊的次數,比如雜湊兩次,相當於 md5(md5(""));
return hashedCredentialsMatcher;
}
/**
* 開啟shiro aop註解支援.
* 使用代理方式;所以需要開啟程式碼支援;
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
這裡再登陸加上
//新增使用者認證資訊
Subject subject = SecurityUtils.getSubject();
SecurityUtils.getSecurityManager().logout(subject);
if(cmsUser!=null){
cmsUser.setSessionId(subject.getSession().getId().toString());
userService.saveCmsUser(cmsUser);
}
//記住我功能,暫時不要
/* String rememberMeAsString = WebUtils.getCleanParam( request, "rememberMe");
boolean rememberMe = false;
if (null != rememberMeAsString) {
rememberMe = Boolean.valueOf(rememberMeAsString);
}*/
AuthenticationToken token = new UsernamePasswordToken(username, PasswordUtil.generatorStoredPwd(passwd));
//進行驗證,這裡可以捕獲異常,然後返回對應資訊
subject.login(token);