springmvc集成shiro登錄失敗處理操作
一般的登錄流程會有:用戶名不存在,密碼錯誤,驗證碼錯誤等..
在集成shiro后,應(yīng)用程序的外部訪問權(quán)限以及訪問控制交給了shiro來管理。
shiro提供了兩個主要功能:認證(Authentication)和授權(quán)(Authorization);認證的作用是證明自身可以訪問,一般是用戶名加密碼,授權(quán)的作用是誰可以訪問哪些資源,通過開發(fā)者自己的用戶角色權(quán)限系統(tǒng)來控制。
shiro的會話管理和緩存管理不在本文范圍內(nèi)。
下面通過登錄失敗的處理流程來介紹springmvc與shiro的集成。
依賴項目
| 依賴名稱 | 版本 |
| spring | 4.1.4.RELEASE |
| shiro | 1.2.2 |
| self4j | 1.7.5 |
| log4j | 1.2.17 |
在web.xml里配置shiro
<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>
新建一個spring-context-shiro.xml配置shiro相關(guān)信息,使用spring加載
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"
default-lazy-init="true">
<description>Shiro Configuration</description>
<!-- 安全認證過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/sys/login" />
<property name="successUrl" value="/sys" />
<property name="filters">
<map> <!--自定義登錄驗證過濾器-->
<entry key="authc" value-ref="formAuthenticationFilter" />
</map>
</property>
<property name="filterChainDefinitions">
<value>
/sys/login = authc
/sys/logout = logout
/sys/** = user
</value>
</property>
</bean>
<!-- 定義 Shiro 主要業(yè)務(wù)對象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="systemAuthorizingRealm" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean>
<!-- 會話ID生成器 -->
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
<!-- 會話管理器,設(shè)定會話超時及保存 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 全局會話超時時間(單位毫秒),默認30分鐘 -->
<property name="globalSessionTimeout" value="1800000" />
<property name="sessionDAO" ref="sessionDAO"/>
</bean>
<!-- 會話驗證調(diào)度器,每30分鐘執(zhí)行一次驗證 -->
<!-- <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> -->
<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
<property name="interval" value="1800000"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!-- sessionDAO保存認證信息 -->
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache" />
<property name="cacheManager" ref="shiroCacheManager" />
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
<!-- 用戶授權(quán)信息Cache, 采用EhCache -->
<bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="cacheManager" />
</bean>
<!-- Shiro生命周期處理器 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- AOP式方法級權(quán)限檢查 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
</beans>
新建一個登錄認證過濾器FormAuthenticationFilter.java
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Service;
/**
* 表單驗證(包含驗證碼)過濾類*/
@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
private String captchaParam = DEFAULT_CAPTCHA_PARAM;
public String getCaptchaParam() {
return captchaParam;
}
protected String getCaptcha(ServletRequest request) {
return WebUtils.getCleanParam(request, getCaptchaParam());
}
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
String locale = request.getParameter("locale");
if (password == null) {
password = "";
}
boolean rememberMe = isRememberMe(request);
String host = getHost(request);
String captcha = getCaptcha(request);
return new UsernamePasswordToken(username, password.toCharArray(),locale, rememberMe, host, captcha);
}
}
新建令牌類UsernamePasswordToken.java
package com.chunhui.webservice.modules.sys.security;
/**
* 用戶和密碼(包含驗證碼)令牌類*/
public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken {
private static final long serialVersionUID = 1L;
private String captcha;
private String locale;
public String getCaptcha() {
return captcha;
}
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public UsernamePasswordToken() {
super();
}
public UsernamePasswordToken(String username, char[] password, boolean rememberMe, String host, String captcha) {
super(username, password, rememberMe, host);
this.captcha = captcha;
}
public UsernamePasswordToken(String username, char[] password, String locale,boolean rememberMe, String host, String captcha) {
super(username, password, rememberMe, host);
this.captcha = captcha;
this.locale = locale;
}
}
最后一個是認證實現(xiàn)類SystemAuthorizationRealm:
package com.chunhui.webservice.modules.sys.security;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import com.chunhui.webservice.common.utils.EmployeeType;
import com.chunhui.webservice.common.utils.VertifyStatus;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import com.chunhui.webservice.common.servlet.ValidateCodeServlet;
import com.chunhui.webservice.common.utils.SpringContextHolder;
import com.chunhui.webservice.modules.sys.entity.Employee;
import com.chunhui.webservice.modules.sys.entity.Menu;
import com.chunhui.webservice.modules.sys.service.SystemService;
import com.chunhui.webservice.modules.sys.utils.SystemUtils;
import com.chunhui.webservice.modules.sys.web.LoginController;
/**
* 系統(tǒng)安全認證實現(xiàn)類*/
@Service
@DependsOn({ "employeeDao", "roleDao", "menuDao" })
public class SystemAuthorizingRealm extends AuthorizingRealm {
private SystemService systemService;
/**
* 認證回調(diào)函數(shù), 登錄時調(diào)用
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
// 判斷驗證碼
Session session = SecurityUtils.getSubject().getSession();
// 設(shè)置獨立的session會話超時時間 session.setTimeout(60000);
String code = (String) session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);
if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)) {
throw new CaptchaException("驗證碼錯誤!");
} //如果帳號不存在,輸出
//throw new UnknownAccountException();
//如果帳號被禁用,輸出
//throw new DisabledAccountException();
//保存登錄時選擇的語言
SecurityUtils.getSubject().getSession().setAttribute("locale", token.getLocale());
try{
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(new Principal(employee), employee.getPassword(), getName());
return info;
}catch (Throwable t){
t.printStackTrace();
throw new AuthenticationException();
}
}/**
* 授權(quán)查詢回調(diào)函數(shù), 進行鑒權(quán)但緩存中無用戶的授權(quán)信息時調(diào)用
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Principal principal = (Principal) getAvailablePrincipal(principals);
Employee employee = getSystemService().getEmployeeByName(principal.getUsername());
if (employee != null) {
SystemUtils.putCache("employee", employee);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<Menu> list = SystemUtils.getMenuList();
for (Menu menu : list) {
if (StringUtils.isNotBlank(menu.getPermission())) {
// 添加基于Permission的權(quán)限信息
for (String permission : StringUtils.split(menu.getPermission(), ",")) {
info.addStringPermission(permission);
}
}
}
// 更新登錄IP和時間
getSystemService().updateEmployeeLoginInfo(employee.getId());
return info;
} else {
return null;
}
}
/**
* 清空用戶關(guān)聯(lián)權(quán)限認證,待下次使用時重新加載
*/
public void clearCachedAuthorizationInfo(String principal) {
SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
clearCachedAuthorizationInfo(principals);
}
/**
* 清空所有關(guān)聯(lián)認證
*/
public void clearAllCachedAuthorizationInfo() {
Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
if (cache != null) {
for (Object key : cache.keys()) {
cache.remove(key);
}
}
}
/**
* 獲取系統(tǒng)業(yè)務(wù)對象
*/
public SystemService getSystemService() {
if (systemService == null) {
systemService = SpringContextHolder.getBean(SystemService.class);
}
return systemService;
}
/**
* 授權(quán)用戶信息
*/
public static class Principal implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String username;
private String realname;
private Map<String, Object> cacheMap;
public Principal(Employee employee) {
this.id = employee.getId();
this.username = employee.getUsername();
this.realname = employee.getRealname();
}
public String getId() {
return id;
}
public String getUsername() {
return username;
}
public String getRealname() {
return realname;
}
public Map<String, Object> getCacheMap() {
if (cacheMap == null) {
cacheMap = new HashMap<String, Object>();
}
return cacheMap;
}
}
}
那么在JSP頁面,可以通過獲取登錄異常具體的異常類型來在頁面顯示錯誤原因
<%String error = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);%>
<c:set var="exp_type" value="<%=error %>"/>
<c:set var="tips" value=""></c:set>
<c:if test="${fn:contains(exp_type,'CaptchaException')}">
<c:set var="tips" value="驗證碼錯誤"></c:set>
</c:if>
<c:if test="${fn:contains(exp_type,'FailVertifyException')}">
<c:set var="tips" value="該賬號審核未通過,不允許登陸!"></c:set>
</c:if>
<c:if test="${fn:contains(exp_type,'NotVertifyException')}">
<c:set var="tips" value="該賬號正在審核中... 不允許登陸!"></c:set>
</c:if>
<c:if test="${fn:contains(exp_type,'UnknownAccountException')}">
<c:set var="tips" value="賬號不存在!"></c:set>
</c:if>
<c:if test="${fn:contains(exp_type,'DisabledAccountException')}">
<c:set var="tips" value="賬號不允許登陸!"></c:set>
</c:if>
<c:if test="${fn:contains(exp_type,'IncorrectCredentialsException')}">
<c:set var="tips" value="密碼錯誤!"></c:set>
</c:if>
以上這篇springmvc集成shiro登錄失敗處理操作就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解springboot和vue前后端分離開發(fā)跨域登陸問題
這篇文章主要介紹了詳解springboot和vue前后端分離開發(fā)跨域登陸問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-09-09
Java技巧函數(shù)方法實現(xiàn)二維數(shù)組遍歷
這篇文章主要介紹了Java技巧函數(shù)方法實現(xiàn)二維數(shù)組遍歷,二維數(shù)組遍歷,每個元素判斷下是否為偶數(shù),相關(guān)內(nèi)容需要的小伙伴可以參考一下2022-08-08
如何解決SpringBoot2.x版本對Velocity模板不支持的方案
這篇文章主要介紹了如何解決SpringBoot2.x版本對Velocity模板不支持的方案,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12
SpringBoot任務(wù)調(diào)度器的實現(xiàn)代碼
SpringBoot自帶了任務(wù)調(diào)度器,通過注解的方式使用。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12
簡單了解springboot中的配置文件相關(guān)知識
這篇文章主要介紹了簡單了解springboot中的配置文件相關(guān)知識,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-11-11
Maven?Web項目使用Cargo插件實現(xiàn)自動化部署的詳細步驟
cargo ,它是一組幫助用戶實現(xiàn)自動化部署,操作Web容器的工具,并且?guī)缀踔С炙械腤eb容器,這篇文章主要介紹了Maven?Web項目使用Cargo實現(xiàn)自動化部署,需要的朋友可以參考下2023-02-02

