您的位置:首页 > 其它

Filter(过滤器)常见应用(三)——权限管理系统(三)

2017-10-14 00:29 671 查看
上一篇文章Filter(过滤器)常见应用(三)——权限管理系统(二)已经开发好了web层的一大部分,做的所有这些工作都是为了这一步——权限实现,是时候使用Filter实现URL级别的权限认证了。


权限管理系统的设计和分析


开发web层


权限实现

现在我们来编写一个过滤器来实现URL级别的权限认证,要在cn.itcast.web.filter包下创建一个Filter——SecurityFilter.java。 


 

编写这个过滤器,还算是有一点麻烦,但是我们按照以下步骤慢慢来,相信我们自己一定能写出来。
检查用户是否已登录。
没登录,登录去。
得到用户想访问的资源。
得到访问该资源需要的权限。
判断用户是否有相应权限。
没有权限,则提示用户权限不足,联系管理员。
如果有,则则放行。

写出来的SecurityFilter过滤器,代码应该就是这样的:
public class SecurityFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub

}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;

// 1. 检查用户是否已登录
User user = (User) request.getSession().getAttribute("user");

// 2. 没登录,登录去
if (user == null) {
request.setAttribute("message", "请先登录!!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}

// 3. 得到用户想访问的资源
String uri = request.getRequestURI();

// 4. 得到访问该资源需要的权限
SecurityService service = new SecurityService();
Resource r = service.findResource(uri);
/*
* 你要访问的资源,我在权限管理系统里面没有说访问这个资源需要权限,
* 也即这个资源不需要被权限系统控制,只有被权限系统控制的资源,在数据库里面
* 才有,如果为null,这个资源不受权限系统控制。
*/
if (r == null) {
chain.doFilter(request, response);
return;
}
Privilege required_Privilege = r.getPrivilege(); // 得到访问资源需要的权限

// 5. 判断用户是否有相应权限
List<Privilege> list = service.getUserAllPrivilege(user.getId()); // 得到用户所有权限
if (!list.contains(required_Privilege)) {
// 6. 没有权限,则提示用户权限不足,联系管理员
request.setAttribute("message", "对不起,您没有权限,请联系管理员!!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}

// 7. 如果有,则则放行
chain.doFilter(request, response);

}

@Override
public void destroy() {
// TODO Auto-generated method stub

}

}
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

代码虽然写出来了,但有一些细节如若我们不注意,那么对这个过滤器的理解依然是不深刻的,这是要不得的。特来对这些细节详加说明。
细节一:在得到访问该资源需要的权限这一步中,我们根据URI查找出的资源,一定得进行非空检查。 

因为对于你要访问的资源,如若我在权限管理系统里面没有说访问这个资源需要权限,也即这个资源不需要被权限系统控制(只有被权限系统控制的资源,在数据库表里面才有),则从数据库表里面查询出来的资源必然为null,如若为null,代表这个资源不受权限系统控制。

细节二:在判断用户是否有相应权限这一步中,用到了这样的代码:
if (!list.contains(required_Privilege)) {
blabla......
}
1
2
3

学过java基础的人应该知道List集合的contains方法内部调用的equals方法,我们自定义的Privilege对象没有重写equals方法(就连hashCode方法也一起重写算了,因为Eclipse会自动帮我们重写这两个方法),就这样比较是不行的。因此,应在Privilege类重写这两个方法。
public class Privilege {

private String id;
private String name; // 添加分类...的权限
private String description;

public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}

// 记住:以后自定义的对象只要涉及到比较,一定要重写hashCode()和equals()这2个方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Privilege other = (Privilege) obj;
if (description == null) {
if (other.description != null)
return false;
} else if (!description.equals(other.description))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

}
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

得出结论:以后自定义的对象只要涉及到比较,一定要重写hashCode()和equals()这2个方法。

编写好这样的过滤器之后,就应在web.xml文件中配置它。
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>cn.itcast.web.filter.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/manager/*</url-pattern>
</filter-mapping>
1
2
3
4
5
6
7
8

这样就可以拦截下manager映射目录下的所有请求,将请求都归在manager映射目录中,可以方便于我们的管理。 

接下来,我们就在WebRoot根目录下新建一个用户登录的页面——login.jsp。 


 

login.jsp页面的内容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/UserServlet?method=login" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="text" name="password"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

当用户输入用户名和密码之后,点击登录按钮,请求同样也应交给UserServlet,又由于请求URL后面的method参数的值是login,因此要把请求派发给login方法处理,这样UserServlet的代码就应该为:
public class UserServlet extends HttpServlet {

private SecurityService service = new SecurityService();

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

String method = request.getParameter("method");
if ("getAll".equals(method)) {
getAll(request, response);
}
if ("addUI".equals(method)) {
addUI(request, response);
}
if ("add".equals(method)) {
add(request, response);
}
if ("forUpdateUserRoleUI".equals(method)) {
forUpdateUserRoleUI(request, response);
}
if ("updateRole".equals(method)) {
updateRole(request, response);
}
if ("login".equals(method)) {
login(request, response);
}
}

private void getAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

List<User> list = service.getAllUser();
request.setAttribute("list", list);
request.getRequestDispatcher("/security/listuser.jsp").forward(request, response);

}

private void addUI(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

request.getRequestDispatcher("/security/adduser.jsp").forward(request, response);

}

private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try {
User user = WebUtils.request2Bean(request, User.class);
user.setId(UUID.randomUUID().toString());
service.addUser(user);

request.setAttribute("message", "添加成功!!!");
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("message", "添加失败!!!");
}
request.getRequestDispatcher("/message.jsp").forward(request, response);

}

private void forUpdateUserRoleUI(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String userid = request.getParameter("id");
User user = service.findUser(userid);

List<Role> list = service.getAllRole();
request.setAttribute("user", user);
request.setAttribute("list", list);
request.getRequestDispatcher("/security/updateUserRole.jsp").forward(request, response);

}

private void updateRole(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try {
String userid = request.getParameter("userid");
String[] rids = request.getParameterValues("rid");
service.updateUserRole(userid, rids);

request.setAttribute("message", "更新成功!!!");
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("message", "更新失败!!!");
}
request.getRequestDispatcher("/message.jsp").forward(request, response);

}

private void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String username = request.getParameter("username");
String password = request.getParameter("password");
User user = service.findUser(username, password);
if (user == null) {
request.setAttribute("message", "用户名或密码错误!!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}

request.getSession().setAttribute("user", user);
response.sendRedirect("/day20/index.jsp");

}

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}
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

用户成功登录之后,就要跳转到网站首页上面去,前面我们就已经写过index.jsp页面了,现在只须修改一下即可。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
欢迎您:${user.username }	<a href="${pageContext.request.contextPath }/UserServlet?method=logout">注销</a>
<br/><br/>
<a href="${pageContext.request.contextPath }/login.jsp">登录</a>
<br/><br/>
<a href="/day20/manager/Servlet1">添加分类</a>
<a href="/day20/manager/Servlet2">删除分类</a>
<a href="/day20/manager/Servlet3">修改分类</a>
<a href="/day20/manager/Servlet4">查找分类</a>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

在修改后的index.jsp页面中我们又添加了一个用户注销的功能,当用户点击注销超链接时,请求同样也应交给UserServlet,又由于请求URL后面的method参数的值是logout,因此要把请求派发给logout方法处理,这样UserServlet的代码就应该为:
public class UserServlet extends HttpServlet {

private SecurityService service = new SecurityService();

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

String method = request.getParameter("method");
if ("getAll".equals(method)) {
getAll(request, response);
}
if ("addUI".equals(method)) {
addUI(request, response);
}
if ("add".equals(method)) {
add(request, response);
}
if ("forUpdateUserRoleUI".equals(method)) {
forUpdateUserRoleUI(request, response);
}
if ("updateRole".equals(method)) {
updateRole(request, response);
}
if ("login".equals(method)) {
login(request, response);
}
if ("logout".equals(method)) {
logout(request, response);
}
}

private void getAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

List<User> list = service.getAllUser();
request.setAttribute("list", list);
request.getRequestDispatcher("/security/listuser.jsp").forward(request, response);

}

private void addUI(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

request.getRequestDispatcher("/security/adduser.jsp").forward(request, response);

}

private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try {
User user = WebUtils.request2Bean(request, User.class);
user.setId(UUID.randomUUID().toString());
service.addUser(user);

request.setAttribute("message", "添加成功!!!");
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("message", "添加失败!!!");
}
request.getRequestDispatcher("/message.jsp").forward(request, response);

}

private void forUpdateUserRoleUI(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String userid = request.getParameter("id");
User user = service.findUser(userid);

List<Role> list = service.getAllRole();
request.setAttribute("user", user);
request.setAttribute("list", list);
request.getRequestDispatcher("/security/updateUserRole.jsp").forward(request, response);

}

private void updateRole(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try {
String userid = request.getParameter("userid");
String[] rids = request.getParameterValues("rid");
service.updateUserRole(userid, rids);

request.setAttribute("message", "更新成功!!!");
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("message", "更新失败!!!");
}
request.getRequestDispatcher("/message.jsp").forward(request, response);

}

private void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String username = request.getParameter("username");
String password = request.getParameter("password");
User user = service.findUser(username, password);
if (user == null) {
request.setAttribute("message", "用户名或密码错误!!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}

request.getSession().setAttribute("user", user);
response.sendRedirect("/day20/index.jsp");

}

private void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {

request.getSession().removeAttribute("user");
response.sendRedirect("/day20/index.jsp");

}

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}
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

连载三篇文章,不得不佩服自己了,终于一个简陋的权限管理系统出炉了,接受大家的检测。


权限实现的细节

即使我们做出了这样一个简陋的权限管理系统,但还有一些细节需要我们注意,我这就来娓娓道来。 

第一,有些人在做权限管理系统时,权限管理系统比较简单,可能就没有角色这个概念,直接就说某个用户拥有某个权限,用户和权限还是多对多的关系,即一个用户可以拥有多个权限,一个权限可以属于多个用户。但这种设计只能做比较简单的权限管理系统,如果一复杂,就会很麻烦,麻烦在什么地方呢?试想一下,现在网站有10个用户,有100个权限值,现在我要给这10个用户都授予100个权限的话,仅在页面里面就要点死人,选中某个用户,给他勾100个权限……这样的话,就要勾1000次,如果使用正规一点的模型的话,就很简单,只需要把这100个权限授予到一个角色上,再把角色给这10个用户就OK了。 

第二,我现在做的权限管理方案属于无侵入式的权限管理方案,即没有侵入到业务代码中去。 

第三,我现在做的权限管理方案属于粗粒度的拦截方案,还有一种拦截方案,即动态代理+注解的拦截方案(细粒度的拦截,可以拦截到某个具体业务方法上)。 

第四,用户要是登录了,若他有几个权限,就可以看到几个超链接,若他没有权限,那这几个超链接就看不见。这个就属于页面显示时候的处理了。我会写一个自定义标签,里面所有的超链接我都使用自定义标签套起来,我这个自定义标签会检查一下用户有没有权限,用户有权限,我就输出标签体,即把这个超链接输出,没有就不输出。 

口说无凭,现在我们来写代码实现第四个细节。按照第四个细节的说法,应写一个自定义标签,所以我们在cn.itcast.web.tag包下创建一个类——PermissionTag.java。 


 

该类须继承SimpleTagSupport,然后再重写doTag方法。
public class PermissionTag extends SimpleTagSupport {

private String value;

public void setValue(String value) {
this.value = value;
}

@Override
public void doTag() throws JspException, IOException {

// 判断用户拥有的权限值中,是否包含value
PageContext pageContext = (PageContext) this.getJspContext();
HttpSession session = pageContext.getSession();
User user = (User) session.getAttribute("user");

if (user != null) {
/*
* 得到用户的所有权限,如果你不想new出这个业务对象的话,也行,
* 这时,用户登录进来了你就要把用户的所有权限找出来,即用户对象里面要有一个集合维护他所有的权限,
* 但不想改动以前的设计了,我就调用service去完成,但这样做有点不太好,即Web层和业务逻辑层绑定在一起了。
*/
SecurityService service = new SecurityService();
List<Privilege> privileges = service.getUserAllPrivilege(user.getId());
boolean b = false;
for (Privilege p : privileges) {
if (p.getName().equals(value)) {
b = true;
break;
}
}

if (b) {
this.getJspBody().invoke(null);
}
}

}

}
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

在得到用户所有权限的时候,注意我们是new出了这个业务对象,如果你不想new出这个业务对象的话,那也行,这时,用户登录进来了你就要把用户的所有权限找出来,即用户对象里面要有一个集合维护他所有的权限,但是我们不想改动以前的设计了,索性干脆就调用service去完成,但这样做有点不太好,即Web层和业务逻辑层绑定在一起了。 

接下来我们就要在WEB-INF目录下新建一个itcast.tld文件,然后在itcast.tld文件中添加对该标签处理类的描述,如下: 


 

itcast.tld文件的内容如下:
<?xml version="1.0" encoding="UTF-8" ?>

<taglib 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-jsptaglibrary_2_1.xsd" version="2.1">

<description>JSTL 1.1 core library</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>c</short-name>
<uri>/itcast</uri>

<tag>
<name>permission</name>
<tag-class>cn.itcast.web.tag.PermissionTag</tag-class>
<body-content>scriptless</body-content>

<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

</taglib>
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

这个时候我们就可以在首页导入并使用自定义标签了。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

欢迎您:${user.username }	<a href="${pageContext.request.contextPath }/UserServlet?method=logout">注销</a>
<br/><br/>
<a href="${pageContext.request.contextPath }/login.jsp">登录</a>
<br/><br/>

<itcast:permission value="添加分类"> <!-- 标签控制用户需要有添加分类的权限值,才可以看到超链接 -->
<a href="/day20/manager/Servlet1">添加分类</a>
</itcast:permission>

<itcast:permission value="删除分类">
<a href="/day20/manager/Servlet2">删除分类</a>
</itcast:permission>

<itcast:permission value="修改分类">
<a href="/day20/manager/Servlet3">修改分类</a>
</itcast:permission>

<itcast:permission value="查找分类">
<a href="/day20/manager/Servlet4">查找分类</a>
</itcast:permission>

<itcast:permission value="删除商品">
<a href="/day20/manager/Servlet5">删除商品</a>
</itcast:permission>
</body>
</html>
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

提示:标签控制用户需要有添加分类的权限值,才可以看到超链接。

最后我多一嘴,有时候权限有可能是一棵树,Privilege类可能还需要这样一个属性
private String module;
来记住权限属于哪个模块。关于这点,我理解不够,无法详细记录了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: