一.認證與授權
認證:系統提供的用於識別用戶身份的功能,通常登錄功能就是認證,讓系統知道你是誰,qq登錄,微信登錄。
授權:系統用戶可以訪問哪些功能,讓系統知道你能做什麼。
shiro核心功能,認證,授權、加密
二.基本使用
1.引入框架依賴
<!-- 引入shiro框架的依賴 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.2</version>
</dependency>
2.在web.xml中配置
<!-- 用於整合shiro框架的過濾器 -->
<servlet>
<servlet-name>shiroFilter</servlet-name>
<servlet-class>org.springframework.web.filter.DelegatingFilterProxy</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>shiroFilter</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
3.在spring配置文件中配置bean,id和必須和我們web.xml中配置的名稱一致,因爲會根據id進行注入否則會報錯
No bean named 'shiroFilter' available
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 安全管理器對象 -->
<property name="securityManager" ref="securityManager"></property>
<!-- 注入相關頁面訪問URL-->
<property name="loginUrl" value="/page/login"></property>
<property name="successUrl" value="/page/index"></property>
<property name="unauthorizedUrl" value="/Unauthorized"></property>
<!-- 注入url攔截規則 -->
<property name="filterChainDefinitions">
<value>
/css/** = anon
/js/** =anon
/images/** =anon
/page/login = anon
/login = anon
/page/equipmentMessage/equipmentDetail = perms["equipment-list"]
/** = authc
</value>
</property>
</bean>
<!-- 註冊安全管理器對象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"></property>
</bean>
<!-- 註冊realm -->
<bean id="realm" class="cn.tlj.login.serviceImpl.LoginRealm"></bean>
shiroFilter中必須要注入安全管理器,否則會報錯,安全管理器對象中需要注入realm來實現我們的認證或是授權的邏輯,當然不注入就什麼也不幹。
package cn.tlj.login.serviceImpl;
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.authc.UsernamePasswordToken;
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 cn.tlj.pojo.RiEquipmentmessageEquipment;
public class LoginRealm extends AuthorizingRealm{
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
//授權方法
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermission("equipment-list");
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//認證方法
//根據用戶名查詢數據庫中usernmae的密碼 框架負責比對密碼是否符合
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
System.out.println("自定義的realm方法執行了");
//第一個參數會被放到theadLocal中,可以在後面取出 第二個參數爲查詢數據庫後得到的密碼 第三個參數爲任意的字符
AuthenticationInfo info = new SimpleAuthenticationInfo(new RiEquipmentmessageEquipment(),"123",this.getName());
//返回空認爲用戶名不存在
return info;
}
}
實現類,我們繼承AuthorizingRealm類,而不是直接實現realm接口。
登錄方法調用subject.login方法進入到realm中的認證與授權方法中。
@RequestMapping("/login")
public String login(String username,String password){
//subject就是對象
Subject subject = SecurityUtils.getSubject();
//創建用戶名密碼令牌 123就是傳入的密碼
AuthenticationToken passwordToken = new UsernamePasswordToken(username,"123");
//高級 沒有報錯就是登陸成功了
try{
subject.login(passwordToken);
}catch(Exception e){
e.printStackTrace();
return "/login/login";
}
//這裏取到後面加入到ThreadLocal中的值
Object principal = subject.getPrincipal();
return "index";
}
org.apache.shiro.authc.UnknownAccountException 未知賬號異常realm返回努力
anon 匿名訪問過濾器(未登錄也可以訪問)
authc 檢查當前用戶是否已登錄
perms 檢查權限 /page/list = perms["staff-list"]
3.配置shiro註解模式
配置類
<!-- spring的代理類 開啓shiro框架註解支持-->
<bean id="defaultAdvisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<!-- 設置爲true強制對目標類進行代理 使用CGIB代理 JDK代理是實現接口-->
<property name="proxyTargetClass" value="true"></property>
</bean>
<!-- 配置shiro框架提供的切面類,用於創建代理對象 通知+切入點=切面 我們前面寫的事務的切面表達式
實際就是代理對象 切入點就是規則 規則滿足了就執行通知的邏輯 創建代理對象 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"></bean>
/**
* Set whether to proxy the target class directly, instead of just proxying
* specific interfaces. Default is "false".
* <p>Set this to "true" to force proxying for the TargetSource's exposed
* target class. If that target class is an interface, a JDK proxy will be
* created for the given interface. If that target class is any other class,
* a CGLIB proxy will be created for the given class.
* <p>Note: Depending on the configuration of the concrete proxy factory,
* the proxy-target-class behavior will also be applied if no interfaces
* have been specified (and no interface autodetection is activated).
* @see org.springframework.aop.TargetSource#getTargetClass()
*/
public void setProxyTargetClass(boolean proxyTargetClass) {
this.proxyTargetClass = proxyTargetClass;
}
默認爲false 爲jdk代理,設置爲true的話強制使用目標源進行代理,使用JDK代理創建是創建一個實現目標對象接口的類(如果目標對象不是接口,那麼會實現其父接口),如果目標對象是類,那麼會使用CGLIB代理方式實現代理(相當於實現一個繼承當前類的類)。 所以這裏要改爲true,使用CGLB模式進行代理,否則會報找不到方法錯誤。
使用方法,在方法上添加註解
@RequestMapping(value="/equipmentMessage/equipmentDetail/list",method = RequestMethod.POST)
@ResponseBody
@RequiresPermissions("equipment-list")
public EquiementPage findEquipmentByCondition(
@RequestParam(value="rows",defaultValue="10")Integer rows,
@RequestParam(value="page",defaultValue="1")Integer page,
@RequestParam(value="workshopName",defaultValue="")String workshopName,
@RequestParam(value="equipmentLevel",defaultValue="")String equipmentLevel,
@RequestParam(value="equipmentName",defaultValue="")String equipmentName)
{
return equipmentService.findEquipmentByCondition(rows, page,workshopName,equipmentLevel,equipmentName);
}
這裏如果沒有對應的權限會報錯: org.apache.shiro.authz.AuthorizationException
我們不希望錯誤返回頁面所以,需要對錯誤進行處理。
SpringMVC錯誤處理:https://blog.csdn.net/afdasfggasdf/article/details/84537386
3.使用shiro提供的頁面標籤方式權限控制
在頁面中加入shiro標籤庫
shiro頁面標籤庫的使用:https://blog.csdn.net/yaodieteng1510/article/details/79992247
4.代碼驗證權限方式(不提倡使用)
Subject subject = SecurityUtils.getSubject(); //內部使用ThreadLocal實現的線程間的通訊
subject .checkPermission("staff-edit"); //沒有權限拋出異常
boolean permitted = SecurityUtils.getSubject().isPermitted("equipment-query-only"); //返回是否有權限