可以处理每次请求的认证。
shiro-拦截器
介绍
需求:
登录:
- 是否请求跳转登录页面。
- 是get的登录请求,返回登录页面。
- 是post的登录请求(输入了账号密码,要登录了)。
- 非登录请求,保存当前请求,重定向到登录页面。
- 是否请求跳转登录页面。
认证:
- 是否携带认证信息
- 未携带认证信息,返回错误码
- 认证信息是否正确
- 执行拦截器链
- 路径匹配
- 是否携带认证信息
implementation
新建模块

导入maven
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<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiaoruiit</groupId>
<artifactId>chapter5-interceptor</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>0.2.23</version>
</dependency>
</dependencies>
<build>
<finalName>chapter5-interceptor</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.8.v20121106</version>
<configuration>
<webAppConfig>
<contextPath>/${project.build.finalName}</contextPath>
</webAppConfig>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/${project.build.finalName}</path>
</configuration>
</plugin>
</plugins>
</build>
</project>web.xml配置
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<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="false">
<!--- shiro 1.2 -->
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<context-param>
<param-name>shiroEnvironmentClass</param-name>
<param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value>
</context-param>
<context-param>
<param-name>shiroConfigLocations</param-name>
<param-value>classpath:shiro-interceptor.ini</param-value>
</context-param>
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>shiro-interceptor.ini配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19[users]
zhang=123,admin
wang=123
[roles]
admin=user:*,menu:*
[filters]
myFilter1=com.xiaoruiit.shiro.filter.MyOncePerRequestFilter
myFilter2=com.xiaoruiit.shiro.filter.MyAdviceFilter
myFilter3=com.xiaoruiit.shiro.filter.MyPathMatchingFilter
myFilter4=com.xiaoruiit.shiro.filter.MyAccessControlFilter
formLogin=com.xiaoruiit.shiro.filter.FormLoginFilter
anyRoles=com.xiaoruiit.shiro.filter.AnyRolesFilter
[urls]
/test.jsp=formLogin,anyRoles[admin,user]
/login.jsp=formLogin
/**=myFilter1,myFilter2,myFilter3[config],myFilter4
#/**=myFilter1动态url拦截器
MyIniWebEnviroment.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
40
41
42
43
44
45
46
47
48
49
50
51package com.xiaoruiit.shiro.env;
import org.apache.shiro.util.ClassUtils;
import org.apache.shiro.web.env.IniWebEnvironment;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import javax.servlet.Filter;
/**
* @author hxr
* @Classname MyIniWebEnviroment
* @Description ToDo
*/
public class MyIniWebEnviroment extends IniWebEnvironment {
// 动态实现url-拦截器的注册。需要将其配置到web.xml中·
@Override
protected FilterChainResolver createFilterChainResolver() {
//在此处扩展自己的FilterChainResolver
//1、创建FilterChainResolver
PathMatchingFilterChainResolver filterChainResolver =
new PathMatchingFilterChainResolver();
//2、创建FilterChainManager
DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager();
//3、注册Filter
for(DefaultFilter filter : DefaultFilter.values()) {
filterChainManager.addFilter(filter.name(), (Filter) ClassUtils.newInstance(filter.getFilterClass()));
}
//4、注册URL-Filter的映射关系
filterChainManager.addToChain("/login.jsp", "authc");
filterChainManager.addToChain("/unauthorized.jsp", "anon");
filterChainManager.addToChain("/**", "authc");
filterChainManager.addToChain("/**", "roles", "admin");
//5、设置Filter的属性
FormAuthenticationFilter authcFilter =
(FormAuthenticationFilter)filterChainManager.getFilter("authc");
authcFilter.setLoginUrl("/login.jsp");
RolesAuthorizationFilter rolesFilter = (RolesAuthorizationFilter)filterChainManager.getFilter("roles");
rolesFilter.setUnauthorizedUrl("/unauthorized.jsp");
filterChainResolver.setFilterChainManager(filterChainManager);
return filterChainResolver;
// return super.createFilterChainResolver();
}
}form表单过滤器
FormLoginFilter.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
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
71package com.xiaoruiit.shiro.filter;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author hxr
* @Classname FormLoginFilter
* @Description ToDo
*/
public class FormLoginFilter extends PathMatchingFilter {
private String loginUrl = "/login.jsp";//跳转失败时的地址
private String successUrl = "/";//成功后跳转的地址
// 请求预处理
// 这里处理登录
@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
if(SecurityUtils.getSubject().isAuthenticated()) {
return true;//已经登录过
}
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if(isLoginRequest(req)) {
if("post".equalsIgnoreCase(req.getMethod())) {//form表单提交
boolean loginSuccess = login(req); //登录
if(loginSuccess) {
return redirectToSuccessUrl(req, resp);
}
}
return true;//继续过滤器链
} else {//保存当前地址并重定向到登录界面
saveRequestAndRedirectToLogin(req, resp);
return false;
}
}
private boolean redirectToSuccessUrl(HttpServletRequest req, HttpServletResponse resp) throws IOException {
WebUtils.redirectToSavedRequest(req, resp, successUrl);
return false;
}
private void saveRequestAndRedirectToLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException {
WebUtils.saveRequest(req);
WebUtils.issueRedirect(req, resp, loginUrl);
}
private boolean login(HttpServletRequest req) {
String username = req.getParameter("username");
String password = req.getParameter("password");
try {
SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password));
} catch (Exception e) {
req.setAttribute("shiroLoginFailure", e.getClass());
return false;
}
return true;
}
private boolean isLoginRequest(HttpServletRequest req) {
return pathsMatch(loginUrl, WebUtils.getPathWithinApplication(req));
}
}AnyRolesFilter
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
47package com.xiaoruiit.shiro.filter;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class AnyRolesFilter extends AccessControlFilter {
private String unauthorizedUrl = "/unauthorized.jsp";
private String loginUrl = "/login.jsp";
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
String[] roles = (String[])mappedValue;
if(roles == null) {
return true;//如果没有设置角色参数,默认成功
}
for(String role : roles) {
if(getSubject(request, response).hasRole(role)) {
return true;
}
}
return false;//跳到onAccessDenied处理
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
Subject subject = getSubject(request, response);
if (subject.getPrincipal() == null) {//表示没有登录,重定向到登录页面
saveRequest(request);
WebUtils.issueRedirect(request, response, loginUrl);
} else {
if (StringUtils.hasText(unauthorizedUrl)) {//如果有未授权页面跳转过去
WebUtils.issueRedirect(request, response, unauthorizedUrl);
} else {//否则返回401未授权状态码
WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
return false;
}
}自定义拦截器
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
28package com.xiaoruiit.shiro.filter;
import org.apache.shiro.web.servlet.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* @author hxr
* @Classname MyOncePerRequestFilter
* @Description
* 自定义的拦截器实现扩展功能
* 1.动态 url -角色/权限访问控制的实现
* 2.根据 Subject 身份信息获取用户信息绑定到 Request
* 3.验证码验证
* 4.在线用户信息的保存
* OncePerRequestFilter 保证一次请求只调用一次 doFilterInternal
* 需要将其配置在ini中
*/
public class MyOncePerRequestFilter extends OncePerRequestFilter {
protected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
System.out.println("=========once per request filter");
chain.doFilter(request, response);
}
}MyAdviceFilter
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
27package com.xiaoruiit.shiro.filter;
import org.apache.shiro.web.servlet.AdviceFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* <p>User: Zhang Kaitao
* <p>Date: 14-2-3
* <p>Version: 1.0
*/
public class MyAdviceFilter extends AdviceFilter {
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
System.out.println("====预处理/前置处理");
return true;//返回false将中断后续拦截器链的执行
}
@Override
protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
System.out.println("====后处理/后置返回处理");
}
@Override
public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
System.out.println("====完成处理/后置最终处理");
}
}MyPathMatchingFilter
1 | package com.xiaoruiit.shiro.filter; |
- MyAccessControlFilter
1 | package com.xiaoruiit.shiro.filter; |
前端
login.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
<style>.error{color:red;}</style>
</head>
<body>
<div class="error">${error}</div>
<form action="" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>test.jsp
1
2
3
4
5<html>
<body>
login success
</body>
</html>
测试
Console
1 | =========once per request filter |