jarniyy的博客

天行健,君子以自强不息

Spring security初探

原文链接:http://yzxqml.iteye.com/blog/1756106

现在很多企业和开发团队都使用了SSH2(Struts 2 +Spring 2.5 +Hibernate)框架来进行开发,  我们或许已经习惯了强大的Spring Framework 全局配置管理,不可否认,Sping是一个很优秀的开源框架,但是由于Spring3.0版本后强大的的注解式bean的诞生,Spring MVC框架这匹黑马正悄然杀起,但今天Spring MVC不是主角,今天我和大家分享一个同样隶属于SpringSource 的安全框架——Spring Security, 下面的基于Spring MVC给大家分享一下Spring Security  的使用。虽然对它的接触时间不长,参考了一些网上朋友的做法,但也按照我的理解把这个框架介绍介绍,不是很专业,还请大家不要介意 。

    

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

 

     首先,我们看web.xml

 

Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  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.     <filter>  
  7.         <filter-name>Set Character Encoding</filter-name>  
  8.         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
  9.         <init-param>  
  10.             <param-name>encoding</param-name>  
  11.             <param-value>UTF-8</param-value>  
  12.         </init-param>  
  13.         <init-param>  
  14.             <param-name>forceEncoding</param-name>  
  15.             <param-value>true</param-value><!-- 强制进行转码 -->  
  16.         </init-param>  
  17.     </filter>  
  18.   
  19.     <filter-mapping>  
  20.         <filter-name>Set Character Encoding</filter-name>  
  21.         <url-pattern>/*</url-pattern>  
  22.     </filter-mapping>  
  23.     <!-- 然后接着是SpringSecurity必须的filter 优先配置,让SpringSecurity先加载,防止SpringSecurity拦截失效-->  
  24.     <filter>  
  25.         <filter-name>springSecurityFilterChain</filter-name>  
  26.         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  27.     </filter>  
  28.   
  29.     <filter-mapping>  
  30.         <filter-name>springSecurityFilterChain</filter-name>  
  31.         <url-pattern>/*</url-pattern>  
  32.     </filter-mapping>  
  33.   
  34.     <welcome-file-list>  
  35.         <welcome-file>index.jsp</welcome-file>  
  36.     </welcome-file-list>  
  37.   
  38.     <!--   
  39.     spring需要加载的配置文件  
  40. -->  
  41.     <context-param>  
  42.         <param-name>contextConfigLocation</param-name>  
  43.         <param-value>   
  44.     WEB-INF/classes/applicationContext.xml,  
  45.     WEB-INF/spring3-servlet.xml,  
  46.     WEB-INF/spring-security.xml   
  47.         </param-value>  
  48.     </context-param>  
  49.   
  50.     <listener>  
  51.         <listener-class>  
  52.             <!--     所以,要在web.xml下面配置好监听,让服务器启动时就初始化改类,可以得到request   -->  
  53.             org.springframework.web.context.request.RequestContextListener  
  54.         </listener-class>  
  55.     </listener>  
  56.     <listener>  
  57.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  58.     </listener>  
  59.   
  60.   
  61.     <!--  
  62.         默认所对应的配置文件是WEB-INF下的{servlet-name}-servlet.xml,这里便是:spring3-servlet.xml  
  63.     -->  
  64.     <servlet>  
  65.         <servlet-name>spring3</servlet-name>  
  66.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  67.         <load-on-startup>1</load-on-startup>  
  68.   
  69.     </servlet>  
  70.   
  71.     <servlet-mapping>  
  72.         <servlet-name>spring3</servlet-name>  
  73.         <!--  
  74.             这里可以用 / 但不能用 /*  
  75.             ,拦截了所有请求会导致静态资源无法访问,所以要在spring3-servlet.xml中配置mvc:resources  
  76.         -->  
  77.         <url-pattern>/</url-pattern>  
  78.     </servlet-mapping>  
  79.   
  80.   
  81.   
  82. </web-app>  

 

     

 

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

 

 

 

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

 

 接着是spring-security.xml

Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  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.     <!--  Spring-Security 的配置 -->  
  10.     <!--  
  11.         注意use-expressions=true.表示开启表达式,否则表达式将不可用. see:http://www.family168.com/tutorial/springsecurity3/html/el-access.html  
  12.     -->  
  13.     <security:http auto-config="true" use-expressions="false" access-denied-page="/user/login_failure.html">  
  14.         <!--允许所有人访问-->  
  15.         <!--     <security:intercept-url pattern="/**" access="permitAll" />-->  
  16.         <!--允许ROLE_ADMIN权限访问-->  
  17.         <security:intercept-url pattern="/user/findAll.html" access="ROLE_ADMIN" />  
  18.         <!--允许ROLE_ADMIN权限访问-->  
  19.         <security:intercept-url pattern="/user/**" access="ROLE_ADMIN" />  
  20.         <!--允许ROLE_USER权限访问-->  
  21.         <security:intercept-url pattern="/success.jsp" access="ROLE_USER,ROLE_ADMIN" />  
  22.         <!--允许IS_AUTHENTICATED_ANONYMOUSLY匿名访问-->  
  23.         <security:intercept-url pattern="/anonymously.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />  
  24.           
  25.         <!-- filters="none"  不过滤这些资源-->  
  26.         <security:intercept-url pattern="/js/**" filters="none" />  
  27.         <security:intercept-url pattern="/index.jsp" filters="none" />  
  28.   
  29.         <!-- login-page:默认指定的登录页面. authentication-failure-url:出错后跳转页面. default-target-url:成功登陆后跳转页面 -->  
  30.         <security:form-login login-page="/index.jsp" authentication-failure-url="/user/login_failure.html"  
  31.             default-target-url="/success.jsp" />  
  32.         <!--  
  33.             invalidate-session:指定在退出系统时是否要销毁Session。logout-success-url:退出系统后转向的URL。logout-url:指定了用于响应退出系统请求的URL。其默认值为:/j_spring_security_logout。  
  34.         -->  
  35.         <security:logout invalidate-session="true" logout-success-url="/index.jsp" logout-url="/j_spring_security_logout" />  
  36.         <!--  
  37.             max-sessions:允许用户帐号登录的次数。范例限制用户只能登录一次。exception-if-maximum-exceeded:  
  38.             默认为false,此值表示:用户第二次登录时,前一次的登录信息都被清空。当exception-if-maximum-exceeded="true"时系统会拒绝第二次登录。  
  39.         -->  
  40.   
  41.         <security:session-management>  
  42.             <security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1" />  
  43.         </security:session-management>  
  44.   
  45.     </security:http>  
  46.     <!-- 指定一个自定义的authentication-manager :customUserDetailsService -->  
  47.     <security:authentication-manager>  
  48.         <security:authentication-provider user-service-ref="customUserDetailsService">  
  49.             <security:password-encoder ref="passwordEncoder" />  
  50.         </security:authentication-provider>  
  51.     </security:authentication-manager>  
  52.   
  53.     <!-- 对密码进行MD5编码 -->  
  54.     <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />  
  55.   
  56.     <!--   
  57.         通过 customUserDetailsService,Spring会控制用户的访问级别.  
  58.         也可以理解成:以后我们和数据库操作就是通过customUserDetailsService来进行关联.  
  59.      -->  
  60.     <bean id="customUserDetailsService" class="org.yzsoft.springmvcdemo.util.CustomUserDetailsService" />  
  61.     <!-- 自定义登陆错误提示,可以取出mymessages.properties的国际化消息-->  
  62.     <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
  63.         <property name="basename" value="classpath:org/yzsoft/springmvcdemo/mymessages" />  
  64.     </bean>  
  65.     <bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" />  
  66. </beans>  

  

 

 这个多解释一下,首先

 

Java代码  收藏代码
  1. <security:intercept-url pattern="/findAll.html" access="hasRole('ROLE_ADMIN')" />  
  2.        <security:intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" />  
  3.        <security:intercept-url pattern="/anonymously.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />  

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

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

 

然后是登陆和安全退出

Java代码  收藏代码
  1. <security:form-login login-page="/index.jsp" authentication-failure-url="/user/login_failure.html"default-target-url="/user/findAll.html" />  
  2. <security:logout invalidate-session="true" logout-success-url="/index.jsp" logout-url="/user/login_failure.html" />  

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

 

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

 

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

Java代码  收藏代码
  1. <security:session-management>  
  2.      <security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1" />  
  3. </security:session-management>  

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

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


 

 

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

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

Java代码  收藏代码
  1. <security:authentication-manager>  
  2.      <security:authentication-provider user-service-ref="customUserDetailsService">  
  3.          <security:password-encoder ref="passwordEncoder" />  
  4.      </security:authentication-provider>  
  5. </security:authentication-manager>  
  6.   
  7.     <!-- 对密码进行MD5编码 -->  
  8. <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />  
  9. <bean id="customUserDetailsService" class="org.yzsoft.springmvcdemo.util.CustomUserDetailsService" />  

 首先是<security:authentication-manager>是指定我们自定义的身份验证策略,这里我们用customUserDetailsService这个bean,就是指向我们CustomUserDetailsService.java这个类。然后<security:password-encoder>指定我们密码使用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(), truetruetruetrue, 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<GrantedAuthority> getAuthorities(Integer role) {  
  73.         System.out.println("取得的权限是  :" + role);  
  74.         List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();  
  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. }  

 这里就是把我们从数据库里面取得的用户权限和Spring Security的配置进行桥接,还记得上面配置文件里的ROLE_ADMIN吧,就是从这里来的,很奇怪的是,这个必须设置成ROLE_ 开头才有效。。郁闷。。。

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

Java代码  收藏代码
  1. TUsers tusers = this.usersService.findByUsername(username);  
  2. user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), truetruetruetrue, 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. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  8. <html>  
  9.   <head>  
  10.     <base href="<%=basePath%>">  
  11.       
  12.     <title>My JSP 'index.jsp' starting page</title>  
  13.     <meta http-equiv="pragma" content="no-cache">  
  14.     <meta http-equiv="cache-control" content="no-cache">  
  15.     <meta http-equiv="expires" content="0">      
  16.     <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">  
  17.     <meta http-equiv="description" content="This is my page">   
  18.   </head>  
  19.     
  20.   <body>  
  21.     用户登陆 <br>  
  22.      ${SPRING_SECURITY_LAST_EXCEPTION.message}  
  23.       <form action="j_spring_security_check" method="post">  
  24.         USERNAME:<input type="text" name="j_username" value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}" /><br/>  
  25.         PASSWORD:<input type="password" name="j_password" value="" /><br/>  
  26.         <input type="checkbox" name="_spring_security_remember_me" />两周之内不必登陆(这个功能没有做的)<br/>  
  27.         <input type="submit">       
  28.     </form>  
  29.   </body>  
  30. </html>  

 

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

 

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

 

——>用户登陆

——> <security:authentication-manager> 拦截

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

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

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

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

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

 

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

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

 

 

 

 

 

2、普通用户登陆



  

 

 

 

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



 

 

 

 

 

3、管理员用户登陆

 

 

 

 

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

 

 

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

 

 

 

 

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

阅读更多
个人分类: Spring Security
想对作者说点什么? 我来说一句

spring-security-3.1.0.RC3

2011年12月05日 15.25MB 下载

Spring Security 资料合集

2015年01月24日 4.94MB 下载

spring security demo

2017年11月12日 358KB 下载

springstarter

2009年12月18日 14.17MB 下载

spring security acl 实例

2017年12月01日 28.3MB 下载

spring security demo2

2017年11月30日 28.95MB 下载

spring security spring security

2009年10月12日 237KB 下载

没有更多推荐了,返回首页

不良信息举报

Spring security初探

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭