一般的登錄流程會(huì)有:用戶名不存在,密碼錯(cuò)誤,驗(yàn)證碼錯(cuò)誤等..
在集成shiro后,應(yīng)用程序的外部訪問權(quán)限以及訪問控制交給了shiro來管理。
shiro提供了兩個(gè)主要功能:認(rèn)證(Authentication)和授權(quán)(Authorization);認(rèn)證的作用是證明自身可以訪問,一般是用戶名加密碼,授權(quán)的作用是誰可以訪問哪些資源,通過開發(fā)者自己的用戶角色權(quán)限系統(tǒng)來控制。
shiro的會(huì)話管理和緩存管理不在本文范圍內(nèi)。
下面通過登錄失敗的處理流程來介紹springmvc與shiro的集成。
依賴項(xiàng)目
依賴名稱 | 版本 |
spring | 4.1.4.RELEASE |
shiro | 1.2.2 |
self4j | 1.7.5 |
log4j | 1.2.17 |
在web.xml里配置shiro
1
2
3
4
5
6
7
8
9
10
11
12
|
< 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 > |
新建一個(gè)spring-context-shiro.xml配置shiro相關(guān)信息,使用spring加載
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
<? 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 > <!-- 安全認(rèn)證過濾器 --> < 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 > <!--自定義登錄驗(yàn)證過濾器--> < 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ù)對(duì)象 --> < bean id = "securityManager" class = "org.apache.shiro.web.mgt.DefaultWebSecurityManager" > < property name = "realm" ref = "systemAuthorizingRealm" /> < property name = "cacheManager" ref = "shiroCacheManager" /> </ bean > <!-- 會(huì)話ID生成器 --> < bean id = "sessionIdGenerator" class = "org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" /> <!-- 會(huì)話管理器,設(shè)定會(huì)話超時(shí)及保存 --> < bean id = "sessionManager" class = "org.apache.shiro.web.session.mgt.DefaultWebSessionManager" > <!-- 全局會(huì)話超時(shí)時(shí)間(單位毫秒),默認(rèn)30分鐘 --> < property name = "globalSessionTimeout" value = "1800000" /> < property name = "sessionDAO" ref = "sessionDAO" /> </ bean > <!-- 會(huì)話驗(yàn)證調(diào)度器,每30分鐘執(zhí)行一次驗(yàn)證 --> <!-- <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保存認(rèn)證信息 --> < 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式方法級(jí)權(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 > |
新建一個(gè)登錄認(rèn)證過濾器FormAuthenticationFilter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
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; /** * 表單驗(yàn)證(包含驗(yàn)證碼)過濾類*/ @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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package com.chunhui.webservice.modules.sys.security; /** * 用戶和密碼(包含驗(yàn)證碼)令牌類*/ 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; } } |
最后一個(gè)是認(rèn)證實(shí)現(xiàn)類SystemAuthorizationRealm:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
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)安全認(rèn)證實(shí)現(xiàn)類*/ @Service @DependsOn ({ "employeeDao" , "roleDao" , "menuDao" }) public class SystemAuthorizingRealm extends AuthorizingRealm { private SystemService systemService; /** * 認(rèn)證回調(diào)函數(shù), 登錄時(shí)調(diào)用 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; // 判斷驗(yàn)證碼 Session session = SecurityUtils.getSubject().getSession(); // 設(shè)置獨(dú)立的session會(huì)話超時(shí)時(shí)間 session.setTimeout(60000); String code = (String) session.getAttribute(ValidateCodeServlet.VALIDATE_CODE); if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)) { throw new CaptchaException( "驗(yàn)證碼錯(cuò)誤!" ); } //如果帳號(hào)不存在,輸出 //throw new UnknownAccountException(); //如果帳號(hào)被禁用,輸出 //throw new DisabledAccountException(); //保存登錄時(shí)選擇的語言 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ù), 進(jìn)行鑒權(quán)但緩存中無用戶的授權(quán)信息時(shí)調(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和時(shí)間 getSystemService().updateEmployeeLoginInfo(employee.getId()); return info; } else { return null ; } } /** * 清空用戶關(guān)聯(lián)權(quán)限認(rèn)證,待下次使用時(shí)重新加載 */ public void clearCachedAuthorizationInfo(String principal) { SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName()); clearCachedAuthorizationInfo(principals); } /** * 清空所有關(guān)聯(lián)認(rèn)證 */ public void clearAllCachedAuthorizationInfo() { Cache<Object, AuthorizationInfo> cache = getAuthorizationCache(); if (cache != null ) { for (Object key : cache.keys()) { cache.remove(key); } } } /** * 獲取系統(tǒng)業(yè)務(wù)對(duì)象 */ 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頁面,可以通過獲取登錄異常具體的異常類型來在頁面顯示錯(cuò)誤原因
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<%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 = "驗(yàn)證碼錯(cuò)誤" ></ c:set > </ c:if > < c:if test = "${fn:contains(exp_type,'FailVertifyException')}" > < c:set var = "tips" value = "該賬號(hào)審核未通過,不允許登陸!" ></ c:set > </ c:if > < c:if test = "${fn:contains(exp_type,'NotVertifyException')}" > < c:set var = "tips" value = "該賬號(hào)正在審核中... 不允許登陸!" ></ c:set > </ c:if > < c:if test = "${fn:contains(exp_type,'UnknownAccountException')}" > < c:set var = "tips" value = "賬號(hào)不存在!" ></ c:set > </ c:if > < c:if test = "${fn:contains(exp_type,'DisabledAccountException')}" > < c:set var = "tips" value = "賬號(hào)不允許登陸!" ></ c:set > </ c:if > < c:if test = "${fn:contains(exp_type,'IncorrectCredentialsException')}" > < c:set var = "tips" value = "密碼錯(cuò)誤!" ></ c:set > </ c:if > |
以上這篇springmvc集成shiro登錄失敗處理操作就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://www.cnblogs.com/nosqlcoco/p/5579081.html