繼續《Shiro基礎應用——角色和權限校驗》中的案例
一、整合到Spring
1.1 導入依賴
<!-- 其他依賴和web集成中 一致 ,此處省略-->
<!-- 新增一個依賴 用於在工廠中生產 ShiroFilter-->
<!-- 會傳遞導入shiro-core 和 shiro-web ,所以這兩個依賴可以不用再導入-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
1.2 配置bean.xml文件
這裏可以單獨設置一個【shiro_bean.xml】文件,然後在【bean.xml】文件中加入如下字段,可以是兩個xml文件關聯到一起
<!--關聯另一個bean.xml-->
<import resource="classpath:shiro_bean.xml"/>
配置文件的邏輯
相關的配置
<!--shiro配置-->
<bean id="myRealm" class="com.rj.realm.MyRealm">
<!--Realm,這樣在myRealm類中就可以不需要啓動三個ContextLoader了-->
<property name="userService" ref="userService"/>
<property name="roleService" ref="roleService"/>
<property name="permissionService" ref="permissionService"/>
<!--身份驗證匹配,主要是導入加密模式等數據-->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<!-- true means hex encoded, false means base64 encoded -->
<property name="storedCredentialsHexEncoded" value="false"/>
<property name="hashIterations" value="10000"/>
</bean>
</property>
</bean>
<!--自定義記住我的cookie的屬性-->
<bean id="remembermecookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<property name="name" value="rememberMe"/>
<property name="httpOnly" value="true"/>
<!-- cookie的生命週期,單位:秒 -->
<property name="maxAge" value="120"/>
</bean>
<!--將自定義好的cookie注入CookieRememberMeManager管理類,然後就可以將其注入到SecurityManager中-->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="remembermecookie"/>
</bean>
<!--自定義session會話管理,同樣由SimpleCookie創建-->
<bean id="sessionId" class="org.apache.shiro.web.servlet.SimpleCookie">
<property name="name" value="sessionId"/>
<property name="httpOnly" value="true"/>
<!-- cookie過期時間,-1:存活一個會話 ,單位:秒,表示cookie結束的時候,會話也結束-->
<property name="maxAge" value="-1"/>
</bean>
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 默認值和配置中給出的一致,所bean:sessionIdCookie 可以省略 -->
<property name="sessionIdCookie" ref="sessionId"/>
<!-- session全局超時時間, 單位:毫秒 ,默認值爲1800000,30分鐘-->
<property name="globalSessionTimeout" value="120000"/>
<!--將監聽器給到sessionManager中-->
<property name="sessionListeners">
<list>
<bean class="com.rj.listener.MySessionListener"/>
</list>
</property>
<!--開啓檢測器,默認開啓,單位毫秒,默認1小時-->
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionValidationInterval" value="40000"/>
</bean>
<!-- 聲明SecurityManager核心組件,將自定義的realm注入到SecurityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
<property name="rememberMeManager" ref="rememberMeManager"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!-- 生產SpringShiroFilter,注入SecurityManager核心組件
( 持有shiro的過濾相關規則,可進行請求的過濾校驗,校驗請求是否合法 )
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/user/login"/>
<property name="unauthorizedUrl" value="/user/error"/>
<!--<property name="" value="/"/>-->
<property name="filterChainDefinitions">
<value>
/user/login = anon
/user/all = authc,roles["管理員"]
</value>
</property>
</bean>
可以發現,與ini配置文件基本一致,需要將【myRealm】、【加密】、【cookie】、【session】等都設置好,然後最終都要注入到【securityManager】中
1.3 配置web.xml文件
之前的【ShiroFileter】和【EnvironmentLoaderListener】可以刪除
<!-- 會從spring工廠中獲取和它同名的bean,(id="shiroFilter")
接到請求後調用bean的doFilter方法,進行訪問控制。
-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- EnvironmentLoaderListener不再需要,因爲shiro環境已由spring初始化
springMVC,spring配置不變 -->
二 、加密
用戶的密碼是不允許明文存儲的,因爲一旦數據泄露,用戶的隱私信息會完全暴露。
密碼必須結果加密,生成密文,然後數據庫中只存儲用戶的密碼的密文。
在加密過程中需要使用到一些**“不可逆加密”**,如 md5,sha等
所謂不可逆是指:
- 加密函數A, 明文 “abc”, A(“abc”) = “密文”,不能通過 “密文” 反推出 “abc”,即使密文泄露密碼仍然安全。
2.1 加密介紹
shiro支持hash(散列)加密,常見的如 md5, sha等
基本加密過程
md5(明文),sha(明文) 得到明文的密文,但明文可能比較簡單導致密文容易被破解。
加鹽加密過程
系統生成一個隨機salt=“xxxxxx”, md5(明文+salt) ,sha(明文+salt),則提升了密文的複雜度。
加鹽多次迭代加密過程
如果迭代次數爲2,則加密2次: md5(明文+salt)=密文a , md5(密文a+salt)=最終密文
sha(明文+salt)=密文a , sha(密文a+salt)=最終密文
則進一步提升了密文的複雜度,和被破解的難度。
加密過程中建議使用salt,並指定迭代次數,迭代次數的建議值1000+
實例代碼:
String password="abc";//密碼明文
String salt=UUID.randomUUID().toString();//鹽
Integer iter = 1000;//迭代次數
String pwd = new Md5Hash(password, salt,iter).toString(); //md5加密
String pwd = new Md5Hash(password, salt, iter).toBase64(); //加密後轉base64
String pwd = new Sha256Hash(password, salt, iter).toString();//sha256加密
String pwd = new Sha256Hash(password, salt, iter).toBase64();//加密後轉base64
String pwd = new Sha512Hash(password, salt, iter).toString();//sha256加密
String pwd = new Sha512Hash(password, salt, iter).toBase64()//加密後轉base64
1.2 加密
增加用戶,或修改用戶密碼時,涉及到密碼的加密
在註冊用戶的業務中,對用戶提交的密碼加密即可。
注意:之前的用戶表,並未考慮存儲加密相關信息,所以此時需要對用戶表做出改進,
加一列【 salt varchar(50) 】,用於存儲每個用戶的鹽。
爲了能夠方便處理【迭代次數】,我們單獨設計工具類。
public class MyConstant {
public static final Integer CONSTANT=10000;
}
service業務層處理加密
@Override
public void add(User user) {
//加密處理
String salt = UUID.randomUUID().toString();
String s = new Md5Hash(user.getPassword(),salt, MyConstant.CONSTANT).toBase64();
user.setPassword(s);
//這裏需要將鹽加入到數據庫,因爲鹽是隨機生成的,要保證登錄沒有問題
user.setSalt(salt);
userDao.add(user);
}
2.3 密碼比對
登錄認證身份時,涉及到密碼 比對 過程
注意,加密過程中使用的加密屬性,和此處使用的加密屬性 必須一致:
【sha256,迭代1000次,使用base64格式化密文】
2.3.1 指定比對器
<!--身份驗證匹配,主要是導入加密模式等數據-->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<!-- <property name="hashAlgorithmName" value="sha-256"/> -->
<!--true標示hex算法,false是base64算法-->
<property name="storedCredentialsHexEncoded" value="false"/>
<property name="hashIterations" value="10000"/>
</bean>
</property>
2.3.2 修改Realm
doGetAuthenticationInfo方法的返回值需要做修改
<bean id="myRealm" class="com.rj.realm.MyRealm">
<!--Realm,這樣在myRealm類中就可以不需要啓動三個ContextLoader了-->
<property name="userService" ref="userService"/>
<property name="roleService" ref="roleService"/>
<property name="permissionService" ref="permissionService"/>
/**
* 既然使用了spring工廠,則可以註解使用業務層成員變量,但是這裏建議使用lombok的@setter
* 也就是不建議使用註解注入,而是在【shiro_bean.xml】文件中注入
* 【注意】必須有【set】方法
*/
@Setter
public class MyRealm extends AuthorizingRealm {
private UserService userService;
private RoleService roleService;
private PermissionService permissionService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("在realm中 查詢權限");
//獲取用戶名
String username = (String) principals.getPrimaryPrincipal();
Set<String> roles = roleService.getRolenameByUsername(username);
Set<String> perms = permissionService.getPernameByUsername(username);
//將查詢的信息封裝,然後返回結果
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(perms);
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("在Realm中查詢身份");
// 獲取用戶登錄時發送來的用戶名
String username = (String) token.getPrincipal();
//在數據庫中查詢信息
User user = userService.findByUsername(username);
//判斷是否存在,如果不存在返回null,該類會在後續直接拋出異常UnknownAccountException
if (user == null) {
return null;
}
//需要加鹽對密碼進行處理
//對鹽進行格式轉化
ByteSource saltBytes = ByteSource.Util.bytes(user.getSalt());
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),saltBytes,this.getName());
}
}
這裏要注意一下,這個【saltBytes】並不是給【user.getPassword】的,因爲從數據庫獲取的密碼已經是加密的了,這裏是給到【shiro_bean.xml】的【credentialsMatcher】屬性進行處理,會給到從前端獲取的【token】的【password】,這樣才能進行比對驗證。
三、記住我RememberMe
在登錄後,可以將用戶名存在cookie中,下次訪問時,可以先不登錄,就可以識別身份。
在確實需要身份認證時,比如購買,支付或其他一些重要操作時,再要求用戶登錄即可,用戶體驗好。
由於可以保持用戶信息,系統後臺也可以更好的監控、記錄用戶行爲,積累數據。
3.1 代碼
”記住我“ 起點在登錄時刻:Subject.login(UsernameAndPasswordToken)
而是否確定要“記住我”,由登錄時的token控制開關:
token.setRememberMe(true);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//如果需要記住我的話,需要在token中設置
token.setRememberMe(true);//shiro默認支持”記住我“,只要有此設置則自動運作。
subject.login(token);
3.2 效果
登錄後效果
3.3 頁面中顯示
在頁面中顯示,cookie中記錄的用戶信息
<shiro:user> 當有記住我信息,或已登錄,則顯示標籤體內容
<shiro:principal> 獲取用戶信息
注意:首頁的訪問路徑的過濾器 不能是 authc,只能是 user 或 anon
<!-- 首頁 xx.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<body>
<h2>Hello World! </h2>
<!-- 重點在此:通過如下shiro標籤顯示 -->
<shiro:user>
歡迎您,<shiro:principal/> <a href="#">退出登錄</a>
</shiro:user>
</body>
</html>
登錄的時候,自動填充用戶名
<!-- 登錄頁面 login.jsp 自動填充用戶名 -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="<c:url value="/user/login"/>" method="post">
<!-- 重點在此:<shiro:principal/> -->
username:<input type="text" name="username" value="<shiro:principal/>"> <br>
password:<input type="password" name="password"><br>
<input type="submit" value="登錄">
</form>
</body>
</html>
3.4 自定義記住我的cookie的時間和名稱
如果需要做自定義,可以明確定義如下兩個組件:
SimpleCookie
:封裝cookie的相關屬性,定製cookie寫出邏輯(詳見:addCookieHeader()
)
CookieRememberMeManager
:接受SecurityManager調度,獲取用戶信息,加密數據,並調度SimpleCookie寫出cookie。
<!-- 之前的配置不變,添加如下配置 -->
<!-- remember me -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<property name="name" value="rememberMe"/>
<property name="httpOnly" value="true"/>
<!-- cookie的生命週期,單位:秒 -->
<property name="maxAge" value="2592000"/><!-- 30天 -->
</bean>
<!--如果要注入到securityManager中,必須先注入到rememberMeManager中-->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!-- 注入SimpleCookie -->
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
...
<!-- CookieRememberMeManager注入給SecurityManager,
SecurityManager在處理login時,如果login成功,則會通過rememberMeManager做"記住我",
將用戶名存入cookie
-->
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
四、Session管理
shiro作爲一款安全管理框架,對狀態保持有很強的需要。
比如最常用的用戶認證,就必需狀態的保持,以及其他的一些功能實現的需要。
【shiro需要:認證中的
記住我中的用戶名
正式登陸的用戶名
】【 開發者需要:其他功能中需要存入session的值 】
shiro提供了一整套session管理方案.
1. shiro的session方案和任何
容器無關
(如servlet容器); 2. javaSE也可以使用;相關組件都是pojo對ioc極其友好(方便的管理對象和滿足依賴關係,定製參數)
3. 可以方便的擴展定製存儲位置(
內存,緩存,數據庫
等) 4. 對
web透明
支持:用了shiro的session後,項目中關於session的代碼完全不用任何改動 5. 提供了全面的session
監聽
機制,和session檢測
機制,對session可以細粒度操作即,使用了shiro後,採用shiro的session方案是最優的方案。
4.1 javaSE環境 (瞭解)
shiro 的session管理方案,可以在javaSE中使用,實用價值不大。
Subject subject = SecurityUtils.getSubject();
//獲取session
Session session = subject.getSession();
//session超時時間,單位:毫秒;0,馬上過期;正數,則空閒對應毫秒後過期;負數,則不會過期
session.setTimeout(10000);
//session存、取值
session.setAttribute("name","zhj");
session.getAttribute("name");
//獲取sessionID
getSession().getId();
//銷燬session
session.stop();
原理,核心對象:
1. SimpleSession
Session的實現類,完成session基本功能。
2. SimpleSessionFactory
生產SimpleSession
3. SessionDAO
默認的實現類:MemorySessionDAO,由SessionManager創建,
負責存儲所有session對象,存儲位置:內存
4. DefaultSessionManager
由SecurityManager創建,負責創建、管理SessionFactory和SessionDAO。
//核心類演示: ( ops:實際開發不用手動創建,shiro會初始化 )
//通過SecurityManager 獲得 SessionManager
DefaultSessionManager sessionManager = (DefaultSessionManager)securityManager.getSessionManager();
//通過SessionManager獲得SessionFactory
SimpleSessionFactory sessionFactory = (SimpleSessionFactory) sessionManager.getSessionFactory();
//通過SessionManager獲得SessionDAO
MemorySessionDAO sessionDAO = (MemorySessionDAO)sessionManager.getSessionDAO();
//通過SessionFactory獲得Session
SimpleSession session1 = (SimpleSession) sessionFactory.createSession(null);
//通過Session做具體操作
session1.setAttribute("name","zhangsan");
System.out.println(session1.getAttribute("name"));
4.2 javaEE環境
<!-- 增加session管理相關配置 -->
<!-- 會話Cookie模板 默認可省-->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<!-- cookie的 key="sid" -->
<property name="name" value="JSESSIONID"/>
<!-- 只允許http請求訪問cookie -->
<property name="httpOnly" value="true"/>
<!-- cookie過期時間,-1:存活一個會話 ,單位:秒 ,默認爲-1-->
<property name="maxAge" value="-1"/>
</bean>
<!--同rememberMe一樣,需要先注入到sessionManager中,然後才能注入到securityManager中-->
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 默認值和配置中給出的一致,所bean:sessionIdCookie 可以省略 -->
<property name="sessionIdCookie" ref="sessionIdCookie"/>
<!-- session全局超時時間, 單位:毫秒 ,30分鐘 默認值爲1800000-->
<property name="globalSessionTimeout" value="1800000"/>
</bean>
<!-- 將sessionManager關聯到SecurityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
...
<!-- 增加配置sessionManager -->
<property name="sessionManager" ref="sessionManager"/>
</bean>
4.3 Session監聽
session有三個核心過程:創建、過期、停止
**過期:**session的默認過期時間爲30分鐘。通過比對最近一次使用時間和當前使用時間判斷
session不會自動報告過期,需檢測器檢測時,或再次訪問時,纔可以識別是否過期並移除。
**停止:**用戶主動
logout
;主動調用session.stop()
; 兩種情況會將session標誌爲停止狀態。
// 定義監聽類 exentends SessionListenerAdapter
//IDEA快捷鍵【ctrl+O】可以選擇繼承方法
public class MySessionListener extends SessionListenerAdapter{
//當有session創建時 觸發
@Override
public void onStart(Session session) {
System.out.println("session:"+session.getId()+" start");
}
//當有session停止時 觸發
@Override
public void onStop(Session session) {
System.out.println("session:"+session.getId()+" stop");
}
//當有session過期時 觸發
// 但不會主動觸發,需要再次訪問時,即又要使用session時纔會發現session過期,並觸發。
@Override
public void onExpiration(Session session) {
System.out.println("session:"+session.getId()+" expired");
}
}
配置監聽類,關聯給SessionManager
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
...
<property name="sessionListeners">
<list>
<bean class="com.zhj.listener.MySessionListener"></bean>
</list>
</property>
...
</bean>
4.4 Session檢測
用戶如果沒有主動退出登錄,只是關閉瀏覽器,則session是否過期無法獲知,也就不能停止session。
爲此,shiro提供了session的檢測機制,可以定時發起檢測,識別session過期 並停止session。
<!-- sessionManager默認開啓session檢測機制 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
...
<!-- 開啓檢測器,默認開啓 -->
<property name="sessionValidationSchedulerEnabled" value="true"/>
<!--- 檢測器運行間隔,單位:毫秒 默認1小時
//檢測到過期後,會直接將session刪除
protected void afterExpired(Session session) {
if (isDeleteInvalidSessions()) {
delete(session);
}
}
-->
<property name="sessionValidationInterval" value="3600000"/>
...
</bean>
如上,通過檢測器,定時的檢測session,並及時移除無效session,釋放資源。
五、註解開發
shiro提供了一系列的訪問控制的註解,可以簡化開發過程。
<!-- 註解加載Controller中,原理是會對Controller做增強,切入訪問控制邏輯,所以需要如下依賴 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
5.1 配置mvc.xml
必須在【mvc.xml】中配置,配置以後,僅僅可以將原【shiro_bean.xml】文件中的【ShiroFilter】中的驗證判斷可以省略。
<!-- enable shiro's annotation-->
<bean id="lifecycleBeanPostProcessor"
class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 自動代理生成器,等價於aop:config;
aop:config 或 AutoProxyCreator兩者選其一,spring官方提醒千萬不要同時使用。
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
-->
<aop:config></aop:config>
<!-- 在此bean的構建過程中,初始化了一些額外功能和piontcut
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
interceptors.add(new UserAnnotationMethodInterceptor(resolver));
interceptors.add(new GuestAnnotationMethodInterceptor(resolver));
-->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
5.2 註解使用
加在類上
@Controller
@RequestMapping("/user")
@RequiresAuthentication //類中的所有方法都需要身份認證
@RequiresRoles(value={"manager","admin"},logical= Logical.OR)//類中的所有方法都需要角色,"或"
public class ShiroController {
...
}
加在方法上
@Controller
@RequestMapping("/user")
public class ShiroController2{
...
@RequiresPermissions({"user:query”,“user:delete"}) //有對應權限,默認是 "且"
@RequiresUser //記住我 或 已身份認證
public String hello(){
...
}
@RequiresGuest //遊客身份
public String hello2(){
...
}
}
配置修改
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!-- 不再需要,此時如果身份或權限不通過,會拋出異常,需要異常解析器處理
<property name="loginUrl" value="/user/login/page"/>
<property name="unauthorizedUrl" value="/error.jsp"/>
<property name="filterChainDefinitions">
<value>
/user/query=anon 如下不再需要,登出可以保留,也可以自己寫handler中subject.logout()
/user/insert=authc,roles["banfu"]
/user/update=authc,perms[""student:update""]
/order/insert=authc,roles["xuewei"]
/user/logout=logout
</value>
</property>-->
</bean>
5.3 異常處理
<!-- mvc.xml中:MVC的自定義異常處理器,用於處理權限或身份認證不通過時的異常處理-->
<bean class="com.zhj.ex.handler.MyExHandler"></bean>
public class MyExceptionResolver implements HandlerExceptionResolver{
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println(ex.getClass());
ex.printStackTrace();//開發時必需
ModelAndView mv = new ModelAndView();
if(ex instanceof IncorrectCredentialsException || ex instanceof UnknownAccountException){
//跳轉登錄頁面,重新登錄
mv.setViewName("redirect:/user/login");
}else if(ex instanceof UnauthorizedException){// 角色不足 權限不足
//跳轉權限不足的頁面
mv.setViewName("redirect:/user/perms/error");
}else if(ex instanceof UnauthenticatedException){//沒有登錄 沒有合法身份
//跳轉登錄頁面,重新登錄
mv.setViewName("redirect:/user/login");
}
return mv;
}
}