doGetAuthorizationInfo和doGetAuthenticationInfo
????這兩個(gè)方法雖然名字很像地啰,但是意義是不一樣的碍彭,doGetAuthorizationInfo方法是進(jìn)行權(quán)限驗(yàn)證透绩,doGetAuthenticationInfo是進(jìn)行身份驗(yàn)證的(登錄驗(yàn)證)
Realm就是負(fù)責(zé)驗(yàn)證的東西,可以是HashMap,SimpleAccountRealm,ini文件,數(shù)據(jù)庫等 最常用的還是數(shù)據(jù)庫.
簡單驗(yàn)證
1.構(gòu)建SecurityManager環(huán)境
? ??DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
2.為SecurityManager設(shè)置檢測源
defaultSecurityManager.setRealm(simpleAccountRealm);
3.設(shè)置SecurityManager
SecurityUtils.setSecurityManager(defaultSecurityManager);
4.獲得用戶主體
Subject subject=SecurityUtils.getSubject();
5.根據(jù)用戶密碼獲得token
UsernamePasswordToken token=new UsernamePasswordToken("Kenny","123456");
6.根據(jù)登陸
subject.login(token);
7.檢測是否登陸成功和權(quán)限問題
subject.isAuthenticated()? ? return boolean
subject.checkRole("admin");檢測角色
subject.checkPermission("user:update");檢測權(quán)限
通過INI文件驗(yàn)證
IniRealm iniRealm=new IniRealm("classpath:user.ini");
ini文件內(nèi)容如下:
[users]
Kenny=111,admin
[roles]
admin=user:delete,user:update
剩下的和之前一樣
通過數(shù)據(jù)庫驗(yàn)證
1.初始化JdbcRealm
JdbcRealm jdbcRealm=new JdbcRealm();
2.為JdbcRealm設(shè)置數(shù)據(jù)源
jdbcRealm.setDataSource(druidDataSource);
3.設(shè)置自定義sql語句查詢是否有該用戶
String sql="select password from test_user where username=?";
jdbcRealm.setAuthenticationQuery(sql);
4.設(shè)置自定義sql語句查詢是否有該角色
String sql="select password from test_user where username=?";
String roleSql="select role_name from test_user_role where user_name=?";
jdbcRealm.setUserRolesQuery(roleSql);
后面都一致
Shiro框架結(jié)合Springboot2.X
1.在pom.xml文件中導(dǎo)入必要的依賴
spring-boot-starter-web
spring-boot-starter-test
spring-boot-starter-thymeleaf
shiro-core
shiro-spring
mysql-connector-java
druid(阿里巴巴的)
下面兩個(gè)是打開aop,實(shí)現(xiàn)注解管理權(quán)限功能
spring-boot-starter-aop
aspectjweaver
redis所需要的依賴(在日常開發(fā)問題文章中有寫)
fastjson
2.設(shè)置自己的Realm
package com.kenny.shiroweb.realm;
import com.kenny.shiroweb.dao.UserDao;
import com.kenny.shiroweb.dao.UserRoleDao;
import com.kenny.shiroweb.vo.User;
import com.kenny.shiroweb.vo.UserRole;
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.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class CustomRealmextends AuthorizingRealm {
MapuserMap=new HashMap(16);
? ? {
userMap.put("Kenny","672332263137c302ab54e66efd791c1b");
? ? ? ? super.setName("customRealm");
? ? }
@Autowired
? ? private UserDaouserDao;
? ? @Autowired
? ? private UserRoleDaoroleDao;
? ? protected AuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principalCollection) {
String userName= (String) principalCollection.getPrimaryPrincipal();
? ? ? ? //從數(shù)據(jù)庫或者緩存中獲取角色數(shù)據(jù)
? ? ? ? Set roles=getRolesByUserName(userName);
? ? ? ? Set permissions=getPermissionsByUserName(userName);
? ? ? ? SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
? ? ? ? simpleAuthorizationInfo.setRoles(roles);
? ? ? ? simpleAuthorizationInfo.setStringPermissions(permissions);
? ? ? ? return simpleAuthorizationInfo;
? ? }
protected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {
//1.從主體傳來得認(rèn)證信息中,獲得用戶名
? ? ? ? String userName=(String)authenticationToken.getPrincipal();
? ? ? ? //2.通過用戶名到數(shù)據(jù)庫中獲取憑證
? ? ? ? String password=getPassworkByUserName(userName);
? ? ? ? if(password==null){
return null;
? ? ? ? }
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(userName,password,"customRealm");
? ? ? ? authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userName));
? ? ? ? return authenticationInfo;
? ? }
private SetgetPermissionsByUserName(String userName) {
Set sets=new HashSet();
? ? ? ? sets.add("user:delete");
? ? ? ? sets.add("user:add");
? ? ? ? return sets;
? ? }
private SetgetRolesByUserName(String userName) {
System.out.println("從數(shù)據(jù)庫中獲取授權(quán)數(shù)據(jù)");
? ? ? ? List list=roleDao.findByUsername(userName);
? ? ? ? List role=new ArrayList<>();
? ? ? ? for(UserRole userRole:list){
role.add(userRole.getRolename());
? ? ? ? }
Set sets=new HashSet<>(role);
? ? ? ? return sets;
? ? }
private StringgetPassworkByUserName(String userName) {
List user=userDao.findByUsername(userName);
? ? ? ? if(user!=null){
return user.get(0).getPassword();
? ? ? ? }
return null;
? ? }
public static void main(String[] args){
Md5Hash md5Hash=new Md5Hash("123","viewer");
? ? ? ? System.out.println(md5Hash.toString());
? ? }
}
密碼加密方式是通過md5加鹽(用戶名)的形式進(jìn)行加密
2.進(jìn)行shiro配置
package com.kenny.shiroweb.config;
import com.kenny.shiroweb.cache.RedisCacheManager;
import com.kenny.shiroweb.filter.RolesOrFilter;
import com.kenny.shiroweb.realm.CustomRealm;
import com.kenny.shiroweb.session.CustomSessionManger;
import com.kenny.shiroweb.session.RedisSessionDao;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
? ? public ShiroFilterFactoryBeanshirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
? ? ? ? ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
? ? ? ? shiroFilterFactoryBean.setSecurityManager(securityManager);
? ? ? ? //自定義攔截器
? ? ? ? Map filtersMap =new LinkedHashMap();
? ? ? ? filtersMap.put("rolesOrFilter", new RolesOrFilter());
? ? ? ? shiroFilterFactoryBean.setFilters(filtersMap);
? ? ? ? //攔截器.
? ? ? ? Map filterChainDefinitionMap =new LinkedHashMap();
? ? ? ? // 配置不會(huì)被攔截的鏈接 順序判斷
? ? ? ? filterChainDefinitionMap.put("/static/**", "anon");
? ? ? ? filterChainDefinitionMap.put("/subLogin", "anon");
? ? ? ? filterChainDefinitionMap.put("/isLogin", "anon");
? ? ? ? //測試權(quán)限訪問
? ? ? ? filterChainDefinitionMap.put("/testConfigRole", "rolesOrFilter[user,admin]");
? ? ? ? //配置退出 過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實(shí)現(xiàn)了
? ? ? ? filterChainDefinitionMap.put("/logout.html", "logout");
? ? ? ? //:這是一個(gè)坑呢启妹,一不小心代碼就不好使了;
//
? ? ? ? filterChainDefinitionMap.put("/**", "authc");
? ? ? ? // 如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.html"頁面
? ? ? ? shiroFilterFactoryBean.setLoginUrl("/login.html");
? ? ? ? // 登錄成功后要跳轉(zhuǎn)的鏈接
? ? ? ? shiroFilterFactoryBean.setSuccessUrl("/index.html");
? ? ? ? //未授權(quán)界面;
? ? ? ? shiroFilterFactoryBean.setUnauthorizedUrl("/403");
? ? ? ? shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
? ? ? ? return shiroFilterFactoryBean;
? ? }
//選擇加密方式
? ? @Bean
? ? public HashedCredentialsMatcherhashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher =new HashedCredentialsMatcher();
? ? ? ? hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這里使用MD5算法;
? ? ? ? hashedCredentialsMatcher.setHashIterations(1);//散列的次數(shù),比如散列兩次框沟,相當(dāng)于 md5(md5(""));
? ? ? ? return hashedCredentialsMatcher;
? ? }
//將自定義的reamlm注冊為bean,并通過hashedCredentialsMatcher進(jìn)行加密
? ? @Bean
? ? public CustomRealmcustomRealm(){
CustomRealm customRealmRealm =new CustomRealm();
? ? ? ? customRealmRealm.setCredentialsMatcher(hashedCredentialsMatcher());
? ? ? ? return customRealmRealm;
? ? }
/**
* 項(xiàng)目自定義的DAO
*/
? ? @Bean
? ? public RedisSessionDaoredisSessionDao() {
return new RedisSessionDao();
? ? }
/**
* 自定義的CacheManager
? ? * @return
? ? */
? ? @Bean
? ? public RedisCacheManagerredisCacheManager() {
return new RedisCacheManager();
? ? }
//自定義sessionmanager
? ? @Bean("sessionManager")
public CustomSessionMangersessionManager() {
CustomSessionManger sessionManager =new CustomSessionManger();
? ? ? ? sessionManager.setSessionDAO(redisSessionDao());
? ? ? ? return sessionManager;
? ? }
/**
* 設(shè)置記住我的cookie
? ? * @return
? ? */
? ? @Bean
? ? public SimpleCookierememberMeCookie(){
//System.out.println("ShiroConfiguration.rememberMeCookie()");
//這個(gè)參數(shù)是cookie的名稱牍氛,對應(yīng)前端的checkbox的name = rememberMe
? ? ? ? SimpleCookie simpleCookie =new SimpleCookie("rememberMe");
? ? ? ? //
? ? ? ? simpleCookie.setMaxAge(259200);
? ? ? ? return simpleCookie;
? ? }
/**
* cookie管理對象;
* rememberMeManager()方法是生成rememberMe管理器晨继,而且要將這個(gè)rememberMe管理器設(shè)置到securityManager中
? ? * @return
? ? */
? ? @Bean
? ? public CookieRememberMeManagerrememberMeManager(){
//System.out.println("ShiroConfiguration.rememberMeManager()");
? ? ? ? CookieRememberMeManager cookieRememberMeManager =new CookieRememberMeManager();
? ? ? ? cookieRememberMeManager.setCookie(rememberMeCookie());
? ? ? ? //rememberMe cookie加密的密鑰 建議每個(gè)項(xiàng)目都不一樣 默認(rèn)AES算法 密鑰長度(128 256 512 位)
? ? ? ? cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
? ? ? ? return cookieRememberMeManager;
? ? }
@Bean
? ? public DefaultWebSecurityManagersecurityManager(){
DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
? ? ? ? securityManager.setRealm(customRealm());
? ? ? ? securityManager.setSessionManager(sessionManager());
? ? ? ? securityManager.setCacheManager(redisCacheManager());
? ? ? ? securityManager.setRememberMeManager(rememberMeManager());
? ? ? ? return securityManager;
? ? }
/**
* Shiro生命周期處理器
? ? * @return
? ? */
? ? @Bean
? ? public LifecycleBeanPostProcessorlifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
? ? }
/**
* 開啟Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP掃描使用Shiro注解的類,并在必要時(shí)進(jìn)行安全邏輯驗(yàn)證
* 配置以下兩個(gè)bean(DefaultAdvisorAutoProxyCreator(可選)和AuthorizationAttributeSourceAdvisor)即可實(shí)現(xiàn)此功能
? ? * @return
? ? */
? ? @Bean
? ? @DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreatoradvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator =new DefaultAdvisorAutoProxyCreator();
? ? ? ? advisorAutoProxyCreator.setProxyTargetClass(true);
? ? ? ? return advisorAutoProxyCreator;
? ? }
@Bean
? ? public AuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =new AuthorizationAttributeSourceAdvisor();
? ? ? ? authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
? ? ? ? return authorizationAttributeSourceAdvisor;
? ? }
}
還有redis的配置文件不在這寫了
通過注解在控制層進(jìn)行攔截
//必須要admin角色才能訪問
@RequiresRoles("admin")?
//要求subject中必須同時(shí)含有file:read和write:aFile.txt的權(quán)限才能執(zhí)行方法
@RequiresPermissions({"user:delete", "user:add"} )
自定義攔截器
創(chuàng)建一個(gè)類繼承AuthorizationFilter并重新他的isAccessAllowed方法,然后在ShiroConfig文件中配置
//自定義攔截器
Map filtersMap =new LinkedHashMap();
filtersMap.put("rolesOrFilter", new RolesOrFilter());
shiroFilterFactoryBean.setFilters(filtersMap);
自定義攔截器代碼
package com.kenny.shiroweb.filter;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class RolesOrFilterextends AuthorizationFilter {
@Override
? ? protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o)throws Exception {
Subject subject=getSubject(servletRequest,servletResponse);
? ? ? ? String[] roles=(String[])o;
? ? ? ? if(roles==null||roles.length==0){
return true;
? ? ? ? }
for (String role : roles){
if(subject.hasRole(role)){
return true;
? ? ? ? ? ? }
}
return false;
? ? }
}
在ShiroConfig中使用:
filterChainDefinitionMap.put("/testConfigRole", "rolesOrFilter[user,admin]");
自定義SessionManger
在ShiroConfig中注冊為bean
package com.kenny.shiroweb.session;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import javax.servlet.ServletRequest;
import java.io.Serializable;
public class CustomSessionMangerextends DefaultWebSessionManager {
@Override
? ? protected SessionretrieveSession(SessionKey sessionKey)throws UnknownSessionException {
Serializable sessionId=getSessionId(sessionKey);
? ? ? ? ServletRequest request=null;
? ? ? ? if(sessionKeyinstanceof WebSessionKey){
request=((WebSessionKey)sessionKey).getServletRequest();
? ? ? ? }
if(request!=null&&sessionId!=null){
Session session=(Session)request.getAttribute(sessionId.toString());
? ? ? ? ? ? if(session!=null){
return session;
? ? ? ? ? ? }
}
Session session=super.retrieveSession(sessionKey);
? ? ? ? if(request!=null&&sessionId!=null){
request.setAttribute(sessionId.toString(),session);
? ? ? ? }
return session;
? ? }
}?
自定義SessionDao
新建一個(gè)類繼承AbstractSessionDAO并重寫他的方法
主要還是增刪改查
key以kenny-session:為前綴加上sessionid
value:為session的序列化
因?yàn)閞edis中不能直接存儲(chǔ)對象,需要將他序列化SerializationUtils.serialize(session);
當(dāng)需要從redis中取出來的時(shí)候再反序列化強(qiáng)轉(zhuǎn)為Session對象即可.
然后將自定義的SessionDao注冊為Bean,
在剛自定義的CustomSessionManger添加自定義的DAO
sessionManager.setSessionDAO(redisSessionDao());
自定義CacheManager
新建一個(gè)類并實(shí)現(xiàn)CacheManager接口
還是通過redis進(jìn)行緩存,與SessionManager類似,只是存儲(chǔ)的對象不一樣
將剛自定義的redisCacheManager在ShiroConfig中注冊為bean
記住我功能
在ShrioConfig文件中添加兩個(gè)Bean
? ??/**
* 設(shè)置記住我的cookie
* @return
*/
@Bean
public SimpleCookierememberMeCookie(){
//System.out.println("ShiroConfiguration.rememberMeCookie()");
//這個(gè)參數(shù)是cookie的名稱,對應(yīng)前端的checkbox的name = rememberMe
? ? SimpleCookie simpleCookie =new SimpleCookie("rememberMe");
? ? //
? ? simpleCookie.setMaxAge(259200);
? ? return simpleCookie;
}
/**
* cookie管理對象;
* rememberMeManager()方法是生成rememberMe管理器搬俊,而且要將這個(gè)rememberMe管理器設(shè)置到securityManager中
* @return
*/
@Bean
public CookieRememberMeManagerrememberMeManager(){
//System.out.println("ShiroConfiguration.rememberMeManager()");
? ? CookieRememberMeManager cookieRememberMeManager =new CookieRememberMeManager();
? ? cookieRememberMeManager.setCookie(rememberMeCookie());
? ? //rememberMe cookie加密的密鑰 建議每個(gè)項(xiàng)目都不一樣 默認(rèn)AES算法 密鑰長度(128 256 512 位)
? ? cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
? ? return cookieRememberMeManager;
}
在securityManager中設(shè)置這個(gè)管理器
securityManager.setRememberMeManager(rememberMeManager());
在控制層上通過
token.setRememberMe(user.isRememberMe());來決定是否開啟這個(gè)功能