一般的登录流程会有:用户名不存在,密码错误,验证码错误等..
在集成shiro后,应用程序的外部访问权限以及访问控制交给了shiro来管理。
shiro提供了两个主要功能:认证(Authentication)和授权(Authorization);认证的作用是证明自身可以访问,一般是用户名加密码,授权的作用是谁可以访问哪些资源,通过开发者自己的用户角色权限系统来控制。
shiro的会话管理和缓存管理不在本文范围内。
下面通过登录失败的处理流程来介绍springmvc与shiro的集成。
依赖项目
依赖名称 | 版本 |
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 > |
新建一个spring-context-shiro.xml配置shiro相关信息,使用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 > <!-- 安全认证过滤器 --> < 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 主要业务对象 --> < 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" /> <!-- 会话管理器,设定会话超时及保存 --> < bean id = "sessionManager" class = "org.apache.shiro.web.session.mgt.DefaultWebSessionManager" > <!-- 全局会话超时时间(单位毫秒),默认30分钟 --> < property name = "globalSessionTimeout" value = "1800000" /> < property name = "sessionDAO" ref = "sessionDAO" /> </ bean > <!-- 会话验证调度器,每30分钟执行一次验证 --> <!-- <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 > <!-- 用户授权信息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式方法级权限检查 --> < 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
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; /** * 表单验证(包含验证码)过滤类*/ @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; /** * 用户和密码(包含验证码)令牌类*/ 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; } } |
最后一个是认证实现类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; /** * 系统安全认证实现类*/ @Service @DependsOn ({ "employeeDao" , "roleDao" , "menuDao" }) public class SystemAuthorizingRealm extends AuthorizingRealm { private SystemService systemService; /** * 认证回调函数, 登录时调用 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; // 判断验证码 Session session = SecurityUtils.getSubject().getSession(); // 设置独立的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(); } } /** * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用 */ @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的权限信息 for (String permission : StringUtils.split(menu.getPermission(), "," )) { info.addStringPermission(permission); } } } // 更新登录IP和时间 getSystemService().updateEmployeeLoginInfo(employee.getId()); return info; } else { return null ; } } /** * 清空用户关联权限认证,待下次使用时重新加载 */ public void clearCachedAuthorizationInfo(String principal) { SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName()); clearCachedAuthorizationInfo(principals); } /** * 清空所有关联认证 */ public void clearAllCachedAuthorizationInfo() { Cache<Object, AuthorizationInfo> cache = getAuthorizationCache(); if (cache != null ) { for (Object key : cache.keys()) { cache.remove(key); } } } /** * 获取系统业务对象 */ public SystemService getSystemService() { if (systemService == null ) { systemService = SpringContextHolder.getBean(SystemService. class ); } return systemService; } /** * 授权用户信息 */ 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页面,可以通过获取登录异常具体的异常类型来在页面显示错误原因
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 = "验证码错误" ></ 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登录失败处理操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://www.cnblogs.com/nosqlcoco/p/5579081.html