Spring security使用实例



今天Spring MVC不是主角,今天我和大家分享一个同样隶属于SpringSource 的安全框架——Spring Security, 下面的基于Spring MVC给大家分享一下Spring Security 的使用。虽然对它的接触时间不长,参考了一些网上朋友的做法,但也按照我的理解把这个框架介绍介绍,不是很专业,还请大家不要介意 。

我们知道,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个资源来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。

首先,我们看web.xml

Java代码
1.
2. 3. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
4. http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
5.
6.
7. Set Character Encoding
8. org.springframework.web.filter.CharacterEncodingFilter
9.
10. encoding 11. UTF-8 12.

13.
14. forceEncoding 15. true
16.

17.

18.
19.
20. Set Character Encoding
21. /*
22.

23.
24.
25. springSecurityFilterChain
26. org.springframework.web.filter.DelegatingFilterProxy
27.

28.
29.
30. springSecurityFilterChain
31. /*
32.

33.
34.
35. index.jsp
36.

37.
38.
41.
42. contextConfigLocation 43. 44. WEB-INF/classes/applicationContext.xml,
45. WEB-INF/spring3-servlet.xml,
46. WEB-INF/spring-security.xml
47.
48.

49.
50. 51. 52.
53. org.springframework.web.context.request.RequestContextListener
54.
55.
56. 57. org.springframework.web.context.ContextLoaderListener 58. 59.
60.
61.
64.
65. spring3
66. org.springframework.web.servlet.DispatcherServlet
67. 1
68.
69.

70.
71.
72. spring3
73.
77. /
78.

79.
80.
81.
82.


xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">


Set Character Encoding
org.springframework.web.filter.CharacterEncodingFilter

encoding UTF-8

forceEncoding true


Set Character Encoding
/*



springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy


springSecurityFilterChain
/*


index.jsp


contextConfigLocation WEB-INF/classes/applicationContext.xml,
WEB-INF/spring3-servlet.xml,
WEB-INF/spring-security.xml


org.springframework.web.context.request.RequestContextListener
org.springframework.web.context.ContextLoaderListener



spring3
org.springframework.web.servlet.DispatcherServlet
1


spring3

/

注释已经写了挺多,还是稍微解释一下要注意的地方,一个是UTF-8编码转换,这个最好加在最前面,让它先生效,我在调试的时候就出过这种情况,web.xml里的其他配置都正常生效了,但是编码死活不行,一中文就乱码,郁闷了老半天,然后突发奇想,是不是web.xml里先声明的配置先生效,后声明的后生效?接着实践,果然不出我所料,把编码转换加在前面,一切正常。。。。我那个晕。。。

关于Spirng MVC的就不说了,那些数据访问、业务和控制层那些的东东就自个研究去了吧。。这不是今天的重点。

接着是spring-security.xml

Java代码
1.
2. 3. xmlns:security="http://www.springframework.org/schema/security"
4. xsi:schemaLocation="http://www.springframework.org/schema/beans
5. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
6. http://www.springframework.org/schema/security
7. http://www.springframework.org/schema/security/spring-security-3.0.xsd">
8.
9.
10.
13.
14.
15.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30. 31. default-target-url="/success.jsp" />
32.
35.
36.
40.
41.
42.
43.

44.
45.

46.
47.
48.
49.
50.

51.

52.
53.
54.
55.
56.
60.
61.
62.
63. 64.

65.
66.


xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/spring-security-3.0.xsd">















default-target-url="/success.jsp" />
















这个多解释一下,首先

Java代码
1.
2.
3.


这个是权限控制,声明了拥有什么权限可以访问哪些资源,这个配置的是有ROLE_ADMIN权限的才可以访问/findAll.html,至于这个ROLE_ADMIN从哪来,呆会再解释。或者像第二句一样配置也可以:拥有ROLE_ADMIN权限的才可以访问/user/下的所有资源,否则都会抛出AccessDeniedException 。

IS_AUTHENTICATED_ANONYMOUSLY就是匿名访问的意思,这个相信都懂的。。。

然后是登陆和安全退出

Java代码
1.
2.

解释下上面一句,相信看也能看出来了的,login-page:默认指定的登录页面. authentication-failure-url:出错后跳转页面(包括那些个啥用户名密码错误吖。。啥啥啥的。). default-target-url:成功登陆后跳转页面 (这里我直接跳到的控制器去查数据列表)。

接着:invalidate-session:指定在退出系统时是否要销毁Session。logout-success-url:退出系统后转向的URL。logout-url:指定了用于响应退出系统请求的URL。其默认值为:/j_spring_security_logout。

接下来是一个比较不错的功能:是否允许同一用户多处登陆

Java代码
1.
2.
3.



exception-if-maximum-exceeded:
默认为false,此值表示:用户第二次登录时,前一次的登录信息都被清空。当error-if-maximum-exceeded=”true”时系统会拒绝第二次登录。


max-sessions:允许用户帐号登录的次数,这里我们允许一次登陆。这里我们做个实验吧,看看是不是真的生效了,请看图

这里我们看到,当同一个账号多处登陆时,就会报出Maximum sessions of 1 for this principal exceeded 的错误,当然,正式使用我们换成mymessages.properties里的我们自定义的国际化消息,这样人性化一点。

好,我们往下看,接着就是应用我们实际项目里的自定义用户权限了

Java代码
1.
2.
3.
4.

5.

6.
7.
8.
9.






首先是是指定我们自定义的身份验证策略,这里我们用customUserDetailsService这个bean,就是指向我们CustomUserDetailsService.java这个类。然后指定我们密码使用MD5进行编码,调用Spring Security自带的MD5加密类。当然,还有加盐MD5或我们自己写的加密算法等安全性更加高的密码策略。这个按项目实际使用配置吧。

然后看到我们的CustomUserDetailsService.java

Java代码
1.package org.yzsoft.springmvcdemo.util;
2.
3.import java.util.ArrayList;
4.import java.util.Collection;
5.import java.util.List;
6.
7.import org.apache.log4j.Logger;
8.import org.springframework.beans.factory.annotation.Autowired;
9.import org.springframework.dao.DataAccessException;
10.import org.springframework.security.core.GrantedAuthority;
11.import org.springframework.security.core.authority.GrantedAuthorityImpl;
12.import org.springframework.security.core.userdetails.User;
13.import org.springframework.security.core.userdetails.UserDetails;
14.import org.springframework.security.core.userdetails.UserDetailsService;
15.import org.springframework.security.core.userdetails.UsernameNotFoundException;
16.import org.yzsoft.springmvcdemo.serviceimpl.UsersServiceImpl;
17.import org.yzsoft.springmvcdemo.vo.TUsers;
18.
19./**
20. * 一个自定义的类用来和数据库进行操作. 即以后我们要通过数据库保存权限.则需要我们继承UserDetailsService
21. *
22. * @author
23. *
24. */
25.public class CustomUserDetailsService implements UserDetailsService {
26.
27. protected static Logger logger = Logger.getLogger(“service”);//log4j,不用解释了吧。。
28. @Autowired
29. private UsersServiceImpl usersService;
30.
31. public UsersServiceImpl getUsersService() {
32. return usersService;
33. }
34.
35. public void setUsersService(UsersServiceImpl usersService) {
36. this.usersService = usersService;
37. }
38.
39. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
40.
41. UserDetails user = null;
42.
43. try {
44.
45. // 搜索数据库以匹配用户登录名.
46. // 我们可以通过dao使用Hibernate来访问数据库
47. System.out.println(username + ” 用户页面输入的用户名”);
48. TUsers tusers = this.usersService.findByUsername(username);
49. System.out.println(tusers.getUsername() + ” 数据库取出的用户名”);
50. // Populate the Spring User object with details from the dbUser
51. // Here we just pass the username, password, and access level
52. // getAuthorities() will translate the access level to the correct
53. // role type
54. // 用户名、密码、是否启用、是否被锁定、是否过期、权限
55. user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), true, true, true, true, getAuthorities(Integer.parseInt(tusers.getRole())));
56.
57.
58. } catch (Exception e) {
59. logger.error(“用户信息错误!”);
60. throw new UsernameNotFoundException(“异常处理:检索用户信息未通过!”);
61. }
62.
63. return user;
64. }
65.
66. /**
67. * 获得访问角色权限列表
68. *
69. * @param access
70. * @return
71. */
72. public Collection getAuthorities(Integer role) {
73. System.out.println(“取得的权限是 :” + role);
74. List authList = new ArrayList();
75.
76. // 所有的用户默认拥有ROLE_USER权限
77. if (role == 0) {
78. System.out.println(“普通用户”);
79. logger.debug(“取得普通用户权限–>”);
80. authList.add(new GrantedAuthorityImpl(“ROLE_USERS”));
81. }
82. // 如果参数role为1.则拥有ROLE_ADMIN权限
83. if (role == 1) {
84. logger.debug(“取得ADMIN用户权限–>”);
85. authList.add(new GrantedAuthorityImpl(“ROLE_ADMIN”));
86. }
87. System.out.println(authList.size()+” 权限列表长度”);
88. return authList;
89. }
90.}
package org.yzsoft.springmvcdemo.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.yzsoft.springmvcdemo.serviceimpl.UsersServiceImpl;
import org.yzsoft.springmvcdemo.vo.TUsers;

/**
* 一个自定义的类用来和数据库进行操作. 即以后我们要通过数据库保存权限.则需要我们继承UserDetailsService
*
* @author
*
*/
public class CustomUserDetailsService implements UserDetailsService {

protected static Logger logger = Logger.getLogger(“service”);//log4j,不用解释了吧。。
@Autowired
private UsersServiceImpl usersService;

public UsersServiceImpl getUsersService() {
return usersService;
}

public void setUsersService(UsersServiceImpl usersService) {
this.usersService = usersService;
}

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {

UserDetails user = null;

try {

// 搜索数据库以匹配用户登录名.
// 我们可以通过dao使用Hibernate来访问数据库
System.out.println(username + ” 用户页面输入的用户名”);
TUsers tusers = this.usersService.findByUsername(username);
System.out.println(tusers.getUsername() + ” 数据库取出的用户名”);
// Populate the Spring User object with details from the dbUser
// Here we just pass the username, password, and access level
// getAuthorities() will translate the access level to the correct
// role type
// 用户名、密码、是否启用、是否被锁定、是否过期、权限
user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), true, true, true, true, getAuthorities(Integer.parseInt(tusers.getRole())));

} catch (Exception e) {
logger.error(“用户信息错误!”);
throw new UsernameNotFoundException(“异常处理:检索用户信息未通过!”);
}

return user;
}

/**
* 获得访问角色权限列表
*
* @param access
* @return
*/
public Collection getAuthorities(Integer role) {
System.out.println(“取得的权限是 :” + role);
List authList = new ArrayList();

// 所有的用户默认拥有ROLE_USER权限
if (role == 0) {
System.out.println(“普通用户”);
logger.debug(“取得普通用户权限–>”);
authList.add(new GrantedAuthorityImpl(“ROLE_USERS”));
}
// 如果参数role为1.则拥有ROLE_ADMIN权限
if (role == 1) {
logger.debug(“取得ADMIN用户权限–>”);
authList.add(new GrantedAuthorityImpl(“ROLE_ADMIN”));
}
System.out.println(authList.size()+” 权限列表长度”);
return authList;
}
}
这里就是把我们从数据库里面取得的用户权限和Spring Security的配置进行桥接,还记得上面配置文件里的ROLE_ADMIN吧,就是从这里来的,很奇怪的是,这个必须设置成ROLE_ 开头才有效。。郁闷。。。

这里我刚开始有一个疑惑,我们看这2句

Java代码
1.TUsers tusers = this.usersService.findByUsername(username);
2.user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), true, true, true, true, getAuthorities(Integer.parseInt(tusers.getRole())));
TUsers tusers = this.usersService.findByUsername(username);
user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), true, true, true, true, getAuthorities(Integer.parseInt(tusers.getRole()))); 这里根本不需要用户输入的密码,只要了用户名,然后直接根据用户名去取权限,就直接设置进Spring Security的User对象里面去,我不禁一身冷汗,这不相当于说有了用户名就直接去查数据库么,而且是不用密码的。。。。

但经过查看官方文档和网上的解释,这才放心,原来是这样的,Spring Security的确是直接根据用户名去查,但是查得出来的Spring Security User对象之后,它会根据这个对象的属性值去数据库查询与这个对象匹配的数据,我们这里设置的是(用户名,密码,是否启用、是否被锁定、是否过期、权限。。。),那么如果数据库存在这个对象,就返回真,否则返回假,这样也就不用担心了,完全可靠。就是我们在前台要做好限制,不能给用户不输密码就访问, 不然挤爆你数据库连接。。。。。

最后登陆页面index.jsp

Java代码
1.<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2.<%
3.String path = request.getContextPath();
4.String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
5.%>
6.
7.
8.
9.
10.
14.
15.
16.
17.
18.
19.
20.
21. 用户登陆

22. ${SPRING_SECURITY_LAST_EXCEPTION.message}
23.

24. USERNAME:

25. PASSWORD:

26. 两周之内不必登陆(这个功能没有做的)

27.
28.

29.
30.
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>










用户登陆
${SPRING_SECURITY_LAST_EXCEPTION.message}

USERNAME:

PASSWORD:

两周之内不必登陆(这个功能没有做的)



这里我还是使用Spring Security默认的j_username和j_password,表单目标也用默认的j_spring_security_check,会默认跳到Spring Security进行拦截。其他的应该不用解释了吧。。。。

最后 ,我们整理一下Spring Security的整个控制过程:

——>用户登陆

——> 拦截

——>交给customUserDetailsService处理,并且声明密码采用MD5策略

——>根据输入的用户名去数据库查这条记录,验证身份

——>取出该条记录的用户权限(锁定、禁用、过期和实际权限等)

——>根据取得的权限列表去security:intercept-url匹配、授权,然后判断是否放行。

这就完成了一整个的权限控制流程。

接下来我们来测试一下看是否真的生效了:

1、测试匿名访问页面,直接地址栏访问:

2、普通用户登陆

然后访问后台管理页面,跳回了登陆页

3、管理员用户登陆

退出后成功跳转回登陆页,点击后退再执行其他操作,这时候session已经注销了的,不能执行,又跳回了登陆页。是我们想要的效果,OK,成功了。

好了,Spring Security的简单使用就讲到这里,其实这只是Spring Security的一小部分,而且这里我还没有用权限表对用户权限进行专门的管理,很多东西还是用Spring Security 默认的,还有Spring Security CAS (单点登陆)以及更加高级的权限控制和更完善的Spring Security 配置,以后我们再慢慢去研究吧。发现Spring Security 这个技术不仅简化了我们的用户权限管理,要知道我们做管理系统的时候这是个大问题,也差不多颠覆了我一贯以来用户权限管理的观念,但是掌握了这种思维之后,又发现,其实,程序并不是只有一种实现方式,它激发了我写程序时要去寻找多种解决方案的想法。学习的路上,就是要不断推翻自己固有的思维,去见识多种新事物,才能有进步。

最后是项目文件,比较大,分卷了,还没有Maven的版本,以后奉上~~~

大小: 70.4 KB
大小: 13.9 KB
大小: 17 KB
大小: 23.5 KB
大小: 23.5 KB
SecurityDemo.part1.rar (9 MB)
下载次数: 539
SecurityDemo.part2.rar (9 MB)
下载次数: 411
SecurityDemo.part3.rar (9 MB)
下载次数: 407
SecurityDemo.part4.rar (9 MB)
下载次数: 372
SecurityDemo.part5.rar (9 MB)
下载次数: 366
SecurityDemo.part6.rar (9 MB)
下载次数: 388
SecurityDemo.part7.rar (1.8 MB)
下载次数: 424
t_users.rar (2 KB)
下载次数: 378
查看图片附件