shiro看了有一段時間了。可是因爲以前對這部分理解不了因此在這上面學習的進展一直很少。可是有了解權限管理在平常開發中很重要,因此硬着頭皮也要啃下來。 實現功能:html
shiro的四大組件:github
Shiro內置過濾器,能夠實現權限相關的攔截器web
經常使用的過濾器:spring
anon: 無需認證(登陸)能夠訪問
sql
authc: 必須認證才能夠訪問
數據庫
user: 若是使用rememberMe的功能能夠直接訪問
apache
perm: 該資源必須獲得資源權限才能夠訪問
api
role: 該資源必須獲得角色權限才能夠訪問
這裏面只用到了身份認證和受權,權限認證只用到了一點點,shiro的原理是封裝的過濾器,他可以在訪問瀏覽器前能過自動完成一些內容。
shiro配置主要兩部分——shiroconfig和自定義的Realm(繼承AuthorizingRealm)
。其中,shiroconfig是shiro的主要配置文件,而自定義的Realm主要是重寫AuthorizingRealm
的兩個方法,分別是身份認證和受權認證調用數據庫查詢比對。而若是須要role訪問則須要重寫一個filter。
項目結構:
環境:
新建表:
對應的bean:
package com.shiro.bean;
public class student {
private String username;
private String password;
private String role;
private String perm;
//省略get set
複製代碼
mybatis簡單查詢:
package com.shiro.mapper;
import com.shiro.bean.student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface studentMapper {
@Select("select * from student where username=#{name}")
student findByName(String name);
}
複製代碼
省略html和sql,詳細能夠到GitHub下載
頁面目錄,:
UserRealm.java
package com.shiro.config;
import com.shiro.bean.student;
import com.shiro.mapper.studentMapper;
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.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/** * 自定義Realm * @author bigsai * */
public class UserRealm extends AuthorizingRealm{
@Autowired(required = false)
private studentMapper studentMapper;
private final Logger logger= LoggerFactory.getLogger(UserRealm.class);
/** * 執行受權邏輯 */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
logger.info("執行邏輯受權");
//給資源進行受權
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加資源的受權字符串
//到數據庫查詢當前登陸用戶的受權字符串
//獲取當前登陸用戶
Subject subject = SecurityUtils.getSubject();
student user = (student) subject.getPrincipal();
student dbUser = studentMapper.findByName(user.getUsername());
info.addRole(user.getRole());//添加role 和perms role表明角色 perms表明操做,或者動做等。用於顆粒化權限管理
info.addStringPermission(dbUser.getPerm());
System.out.println("user:"+dbUser.getPerm());
return info;
}
/** * 執行認證邏輯 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("執行認證邏輯");
//編寫shiro判斷邏輯,判斷用戶名和密碼
//1.判斷用戶名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;
student user = studentMapper.findByName(token.getUsername());
if(user==null){
//用戶名不存在
return null;//shiro底層會拋出UnKnowAccountException
}
//2.判斷密碼
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
複製代碼
rolesFilter
package com.shiro.config;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
// AuthorizationFilter抽象類事項了javax.servlet.Filter接口,它是個過濾器。
public class rolesFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception {
Subject subject = getSubject(req, resp);
String[] rolesArray = (String[]) mappedValue;
if (rolesArray == null || rolesArray.length == 0) { //沒有角色限制,有權限訪問
return true;
}
for (int i = 0; i < rolesArray.length; i++) {
if (subject.hasRole(rolesArray[i])) { //若當前用戶是rolesArray中的任何一個,則有權限訪問
return true;
}
}
return false;
}
}
複製代碼
shiroConfig:shiro的主要配置
package com.shiro.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
/** * Shiro的配置類 * * @author bigsai */
@Configuration
public class ShiroConfig {
/** * 建立ShiroFilterFactoryBean */
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//設置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, Filter> filtersMap = new LinkedHashMap<>();
filtersMap.put("rolesFilter",new rolesFilter());
shiroFilterFactoryBean.setFilters(filtersMap);//使用自定義fitter
//添加Shiro內置過濾器
/** * Shiro內置過濾器,能夠實現權限相關的攔截器 * 經常使用的過濾器: * anon: 無需認證(登陸)能夠訪問 * authc: 必須認證才能夠訪問 * user: 若是使用rememberMe的功能能夠直接訪問 * perm: 該資源必須獲得資源權限才能夠訪問 * role: 該資源必須獲得角色權限才能夠訪問 */
Map<String, String> filterMap = new LinkedHashMap<String, String>();
filterMap.put("/login", "anon");//要將登錄的接口放出來,否則沒權限訪問登錄的接口
filterMap.put("/getcontroller", "anon");
//
//受權過濾器
//注意:當前受權攔截後,shiro會自動跳轉到未受權頁面
filterMap.put("/add", "perms[add]");
filterMap.put("/update", "perms[update]");
//
filterMap.put("/test1.html","rolesFilter[admin,user]");
filterMap.put("/*", "authc");//authc即爲認證登錄後便可訪問
//修改調整的登陸頁面
shiroFilterFactoryBean.setLoginUrl("/index");
//設置未受權提示頁面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/** * 建立DefaultWebSecurityManager */
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//關聯realm
securityManager.setRealm(userRealm);
return securityManager;
}
/** * 建立Realm */
@Bean(name = "userRealm")
public UserRealm getRealm() {
return new UserRealm();
}
}
複製代碼
身份認證,就是登陸校檢。這是第一層過濾,而且當用戶沒有登陸的時候,回退到沒登錄的界面。在controller中,login的核心爲:
@RequestMapping("/login")
public String login(String name, String password, Model model, HttpServletRequest request) {
model.addAttribute("nama", "給個star");
/** * 使用Shiro編寫認證操做 */
//1.獲取Subject
Subject subject = SecurityUtils.getSubject();
//2.封裝用戶數據
UsernamePasswordToken token = new UsernamePasswordToken(name, password);
//3.執行登陸方法
try {
subject.login(token);
//登陸成功
//跳轉
return "redirect:/index2";
} catch (UnknownAccountException e) {
//e.printStackTrace();
//登陸失敗:用戶名不存在
model.addAttribute("msg", "用戶名不存在");
return "login";
} catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登陸失敗:密碼錯誤
model.addAttribute("msg", "密碼錯誤");
return "login";
}
}
複製代碼
releam中
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("執行認證邏輯");
//編寫shiro判斷邏輯,判斷用戶名和密碼
//1.判斷用戶名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;
student user = studentMapper.findByName(token.getUsername());
if(user==null){
//用戶名不存在
return null;//shiro底層會拋出UnKnowAccountException
}
//2.判斷密碼
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
複製代碼
而這只是表象的處理過程,而在releam(繼承AuthorizingRealm
)中須要充血doGetAuthenticationInfo()
方法.
大體流程爲:登陸
——>拿帳號密碼檢驗
———>用着token的帳號經過你的sql查詢對象
——>比對數據是否一致
——>經過仍是拋各類異常
而在shiroConfig中,基於url過濾時authc
便可訪問
可能會遇到以下狀況:教師端,學生端來自兩張表,兩個登陸接口,我該如何使用shiro身份認證。對於這種問題,你能夠配置多個releam,可是我以爲若是簡單你能夠在不一樣的登陸接口下傳遞一個參數過來,這個參數就用session傳遞。由於,shiro的session和網頁httprequest得到的session是同一個session
。
因此當你在login傳遞一個屬性到releam中,可用 if else判斷而後不一樣登陸接口執行不一樣的查詢方法便可。
接上流程 是否登陸
——>是/否
——(是)—>查詢role/perm添加到subject
——>過濾器校驗該url須要權限
——>能夠訪問/權限不足
shiro主要url能夠根據角色(role)和資源(perm)的管理。對於role,能夠是管理員,教師等,而perm,多是一個動做,一個操做,等等。==而且可能一個角色擁有多個role和perm==。 同理,受權就是查詢數據庫的role或者perm字段添加到角色中。固然具體api不作介紹。 主要方法爲上述:
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
logger.info("執行邏輯受權");
//給資源進行受權
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加資源的受權字符串
//到數據庫查詢當前登陸用戶的受權字符串
//獲取當前登陸用戶
Subject subject = SecurityUtils.getSubject();
student user = (student) subject.getPrincipal();
student dbUser = studentMapper.findByName(user.getUsername());
info.addRole(user.getRole());//添加role 和perms role表明角色 perms表明操做,或者動做等。用於顆粒化權限管理
info.addStringPermission(dbUser.getPerm());
System.out.println("user:"+dbUser.getPerm());
return info;
}
複製代碼
而url中也是
filterMap.put("/add", "perms[add]");
filterMap.put("/update", "roles[admin]");
複製代碼
經常遇到這種狀況:一個接口/頁面,有兩個或者以上角色能夠訪問。而後再後臺的過濾器配置總。shiro默認的配置是and而不是or。這就須要咱們本身定義filter繼承AuthorizationFilter
從寫對應方法。
以多角色訪問爲例子。從寫上述就是文件rolesFilter。在使用的時候也要首先聲明filter才能使用。
在頁面受權的
運行測試:訪問其餘接口都被返回到這個界面
登錄成功後,界面能夠訪問
有個小注意點:若是mybatis2.0版本回和spring-start-web有衝突。我用1.3.2版本沒問題。