Spring Security 是 Spring Framework 的一个子项目. 之前也叫做 Acegi Secruty.

Spring Security 能以声明的方式来保护 Web 应用程序的 URL 访问. 只需简单的配置即可实现.

Spring Security 通过一系列 Servlet 过滤器为 Web 应用程序提供了多种安全服务.

 

Spring Security 使用认证过程过滤器(AuthenticationProcessingFilter) 来处理表单认证,

如果要读源码,从这个类读起。

 

权限判断流程 :FilterSecurityInterceptor(过滤器安全拦截器), 读源码,从这读起。

 

 

配置示例:

       web.xml

      


view plain copy to clipboard print ?



1. <!-- 加入spring应用 -->  
2. <listener>  
3.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
4. </listener>  
5. <context-param>  
6.     <param-name>contextConfigLocation</param-name>  
7.     <param-value>/WEB-INF/applicationContext.xml</param-value>  
8. </context-param>  
9. <!-- 加入spring-security-->  
10.   <filter>  
11.     <filter-name>springSecurityFilterChain</filter-name>  
12.     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
13. </filter>  
14. <filter-mapping>  
15.     <filter-name>springSecurityFilterChain</filter-name>  
16.     <url-pattern>/*</url-pattern>  
17. </filter-mapping>


<!-- 加入spring应用 --><listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value></context-param><!-- 加入spring-security--> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern></filter-mapping> 

      

       applicationContext.xml

        


view plain copy to clipboard print ?



1. <!-- 头信息 -->   
2. <beans xmlns="http://www.springframework.org/schema/beans"   
3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
4.     xmlns:sec="http://www.springframework.org/schema/security"   
5.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
6.     http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.2.xsd">  
7. <!-- 配置 http 的安全信息 -->   
8. <sec:http auto-config="true">   
9.     <!-- 1. 需要保护哪些页面, 这些页面哪些角色可以访问 -->   
10.     <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <!-- 表示不需要任何权限(不需要登陆)即可以访问 -->  
11.     <sec:intercept-url pattern="/admin.jsp" access="ROLE_ADMIN"/>    
12.     <sec:intercept-url pattern="/index.jsp" access="ROLE_USER"/>  <!-- 可以逗号分隔多个角色  -->  
13. </sec:http>   
14. <sec:authentication-provider>   
15.     <sec:user-service>   
16.         <!-- 2. 配置用户信息: 用户名, 密码; 以及该用户是什么样的角色 -->   
17.         <sec:user password="admin" name="admin" authorities="ROLE_ADMIN, ROLE_USER"/>  
18.         <sec:user password="user" name="user" authorities="ROLE_USER"/>   
19.     </sec:user-service>   
20. </sec:authentication-provider>


<!-- 头信息 --><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.2.xsd"><!-- 配置 http 的安全信息 --><sec:http auto-config="true"> <!-- 1. 需要保护哪些页面, 这些页面哪些角色可以访问 --> <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <!-- 表示不需要任何权限(不需要登陆)即可以访问 --> <sec:intercept-url pattern="/admin.jsp" access="ROLE_ADMIN"/> <sec:intercept-url pattern="/index.jsp" access="ROLE_USER"/> <!-- 可以逗号分隔多个角色 --></sec:http><sec:authentication-provider> <sec:user-service> <!-- 2. 配置用户信息: 用户名, 密码; 以及该用户是什么样的角色 --> <sec:user password="admin" name="admin" authorities="ROLE_ADMIN, ROLE_USER"/> <sec:user password="user" name="user" authorities="ROLE_USER"/> </sec:user-service></sec:authentication-provider> 

      

以上配置完成后,当访问index.jsp或/admin.jsp会进入到一个登陆页面(spring自动生成的,我们也可以自己指定)

当用user用户登陆后,访问admin.jsp时会提示403,无法访问,没有权限。

使用admin时,可以风雨无阻。

 

在<sec:http>配置:

  <sec:form-login login-page="/login.jsp"/>

       表示用logon.jsp页面登陆。默认spring会自己生成一个登陆页面。但是,我们也可以通过这种方式自己指定。

       但是 , 页面中元素的命名等需要遵循某些规范。例如form的action,User项对应的文本框必须name为j_username等等。

       这些可以通过查看spring生成的登陆页面源码来探知详细。

其它属性:

login-processing-url,例如:

       login-processing-url="/loginTo" ---> 表示当访问"/loginTo"这个url模式时,跳转到此登陆页面

 

default-target-url,例如:

       default-target-url="/success.jsp"  ---> 表示当登陆成功后,跳转到/success.jsp页面。注意,如果这个页面也是受保护的话,可能导致某些用户登录后提示错误。

       默认情况:默认情况,也就是说如果没有配置这个属性的话。有两种情况:

              1. 当在未登录的情况下,去访问一个受保护的页面。这个时候,理所当然,他会被强制登录。

                 而,这个时候,用户如果登录成功,页面将重新回到他要访问的那个页面。

                    例如,当用户未登录访问admin.jsp ---> 页面跳转到login.jsp  ---> 以admin账号登录成功 ---> 页面回到admin.jsp

                 虽然如此,但用户是否访问页面成功,依然视其权限而定。

             

              2. 当未登录而直接访问登录页面时,登录成功后,会跳转到WEB的根目录,如果目录下游index.jsp、index.html等主页时,将被访问。

 

always-use-default-target , 例:

       always-use-default-target="true"

       表示,登录成功后,始终使用“login-processing-url”的页面作为登录成功页面。

 

<sec:logout logout-url="/logout"/>

表示,当访问“/logout”的时候,登出当前登陆。

默认情况:也就是logout-url缺省时:

默认被映射到 /j_spring_security_logout

 

其它:

invalidate-session="true" 表示登出时销毁session

logout-success-url="/success.jsp" 表示登出成功显示的页面

 

 

**************

JSP页面 根据权限动态显示(登录用户有权限时则显示、否则不显示):

 

<security:authorize ifAnyGranted="ROLE_DELETE, ROLE_UPDATE">

       <th>操作</th>

</security:authorize>

    ---- 当用户具有 ROLE_DELETE, ROLE_UPDATE 之间任意一个权限,则显示 <th>操作</th>

 

 

<security:authorize ifAllGranted="ROLE_UPDATE">

       <a href="${cp }/role-update-ui.do?roleid=${role.id}">修改</a>

</security:authorize>

   ----  当用户具有ROLE_UPDATE权限时,才显示标签内的内容。(还可以逗号隔开多个权限,此时登录用户必须满足所有权限,才满足显示条件)

 

 

***************

web.xml配置


view plain copy to clipboard print ?



  1. <filter>  
  2.     <filter-name>springSecurityFilterChain</filter-name>  
  3.     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  4. </filter>  
  5. <filter-mapping>  
  6.   <filter-name>springSecurityFilterChain</filter-name>  
  7.   <url-pattern>/*</url-pattern>  
  8. </filter-mapping>  


<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 

 


***************

程序员可以自定义 UserDetailsService 接口的实现类, 将该类配置到 Spring 的 IOC 容器中:

 在 <authentication-provider user-service-ref=“”> 节点的 user-service-ref 属性中引用该 Bean. 即可实现自定义的登录.

 

例:

  =====applicationContext-security.xml  ---

      

<http auto-config="true">

       <intercept-url pattern="/index.jsp*" access="ROLE_USER" />

       <intercept-url pattern="/admin.jsp*" access="ROLE_ADMIN" />

       <!-- 登出 -->

       <logout logout-url="/logout"/>           

</http>

<authentication-provider user-service-ref="detailsService" />

<b:bean id="detailsService" class="security.service.UserDetailsServiceImpl" />

 

类:

   =====security.service.UserDetailsServiceImpl


view plain copy to clipboard print ?



  1. public class UserDetailsServiceImpl implements UserDetailsService {  
  2.    
  3.        @Override  
  4.        public UserDetails loadUserByUsername(String arg0)  
  5.                      throws UsernameNotFoundException, DataAccessException {  
  6.               GrantedAuthority[] auths = new GrantedAuthority[2];  
  7.               auths[0] = new GrantedAuthorityImpl("ROLE_USER");  
  8.               auths[1] = new GrantedAuthorityImpl("ROLE_ADMIN");  
  9.               User user = new User("wjh", "422899", true, auths );  
  10.               return user;  
  11.        }  
  12.    
  13. }  


public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String arg0) throws UsernameNotFoundException, DataAccessException { GrantedAuthority[] auths = new GrantedAuthority[2]; auths[0] = new GrantedAuthorityImpl("ROLE_USER"); auths[1] = new GrantedAuthorityImpl("ROLE_ADMIN"); User user = new User("wjh", "422899", true, auths ); return user; } } 

 

以上代码等价于如下配置:


view plain copy to clipboard print ?



  1. <http auto-config="true">  
  2.        <intercept-url pattern="/index.jsp*" access="ROLE_USER" />  
  3.        <intercept-url pattern="/admin.jsp*" access="ROLE_ADMIN" />  
  4.        <!-- 登出 -->  
  5.        <logout logout-url="/logout"/>             
  6. </http>  
  7. <authentication-provider>  
  8.        <user-service>  
  9.               <user password="admin" name="admin" authorities="ROLE_ADMIN, ROLE_USER" />  
  10.               <user password="user" name="user" authorities="ROLE_USER" />  
  11.        </user-service>  
  12. </authentication-provider>  


<http auto-config="true"> <intercept-url pattern="/index.jsp*" access="ROLE_USER" /> <intercept-url pattern="/admin.jsp*" access="ROLE_ADMIN" /> <!-- 登出 --> <logout logout-url="/logout"/> </http><authentication-provider> <user-service> <user password="admin" name="admin" authorities="ROLE_ADMIN, ROLE_USER" /> <user password="user" name="user" authorities="ROLE_USER" /> </user-service></authentication-provider> 

 

=======它的优势在于

       我们可以在自实现的UserDetailsService 类中访问数据库,以此获取权限信息(登录名、密码总算来自数据库了)。

 

***********************

使用数据库管理用户权限

 

数据结构:

create table user(

    id bigint,

    username varchar(50),

    password varchar(50),

    status integer,

    descn varchar(200)

);

 

create table user_role(

    user_id bigint,

    role_id bigint

);

 

create table role(

    id bigint,

    name varchar(50),

    descn varchar(200)

);

 

 

类:

   =====security.service.UserDetailsServiceImpl


view plain copy to clipboard print ?



  1. public class UserDetailsServiceImpl implements UserDetailsService {  
  2.         
  3.        //通过依赖注入的方式,获取dbcTemplate   
  4.        private SimpleJdbcTemplate jdbcTemplate;  
  5.        public void setJdbcTemplate(SimpleJdbcTemplate jdbcTemplate) {  
  6.               this.jdbcTemplate = jdbcTemplate;  
  7.        }  
  8.         
  9.        @SuppressWarnings("deprecation")  
  10.        @Override  
  11.        public UserDetails loadUserByUsername(String username)  
  12.                      throws UsernameNotFoundException, DataAccessException {  
  13.               String sql = "select username, password, status, name " +  
  14.                      "from user u " +  
  15.                      "join user_role ur " +  
  16.                      "on  u.id = ur.user_id " +  
  17.                      "join role r " +  
  18.                      "on r.id = ur.role_id where u.username = ?";  
  19.               //查询出将要用到的信息   
  20.               List<Map<String, Object>> roleList = jdbcTemplate.queryForList(sql, username);  
  21.               if(roleList.size() <= 0) {  
  22.                      throw new UsernameNotFoundException("用户不存在,或密码错误!");  
  23.               }  
  24.                
  25.               String password = (String) roleList.get(0).get("password");  
  26.               boolean enabled = ((Integer) roleList.get(0).get("status") == 1);  
  27.                
  28.               List<GrantedAuthority> anthoritieList = new ArrayList<GrantedAuthority>();  
  29.               for (Map<String, Object> map : roleList) {  
  30.                      String role = (String)map.get("name");  
  31.                      anthoritieList.add(new GrantedAuthorityImpl(role));  
  32.               }  
  33.                
  34.               return new User(username,password,enabled,anthoritieList.toArray(new GrantedAuthority[0]));  
  35.        }  
  36.    
  37. }  


public class UserDetailsServiceImpl implements UserDetailsService { //通过依赖注入的方式,获取dbcTemplate private SimpleJdbcTemplate jdbcTemplate; public void setJdbcTemplate(SimpleJdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @SuppressWarnings("deprecation") @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { String sql = "select username, password, status, name " + "from user u " + "join user_role ur " + "on u.id = ur.user_id " + "join role r " + "on r.id = ur.role_id where u.username = ?"; //查询出将要用到的信息 List<Map<String, Object>> roleList = jdbcTemplate.queryForList(sql, username); if(roleList.size() <= 0) { throw new UsernameNotFoundException("用户不存在,或密码错误!"); } String password = (String) roleList.get(0).get("password"); boolean enabled = ((Integer) roleList.get(0).get("status") == 1); List<GrantedAuthority> anthoritieList = new ArrayList<GrantedAuthority>(); for (Map<String, Object> map : roleList) { String role = (String)map.get("name"); anthoritieList.add(new GrantedAuthorityImpl(role)); } return new User(username,password,enabled,anthoritieList.toArray(new GrantedAuthority[0])); } } 

 

 

****************************************

如何动态加入受保护资源信息:

方式:

   程序员可以自定义 ObjectDefinitionSource 接口的实现类,

   将该类的实例装配给 FilterSecurityInterceptor 的 objectDefinitionSource 的属性.

   即可实现自定义的资源获取.

 

分析过程:

   通过读源码:

     FilterSecurityInterceptor --> doFilter --> invoke --> super.beforeInvocation(fi) -->|

        ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);

    

     最后一句代码:this.obtainObjectDefinitionSource().getAttributes(object) 的 obtainObjectDefinitionSource()

     这是 FilterSecurityInterceptor 的一个方法,同时此类还有一个 setObjectDefinitionSource 方法, 给了我们用依赖注入的方式改变其值的可能。

     方法返回一个ObjectDefinitionSource的实例

     通过 Debug 看到,实际上返回的是 DefaultFilterInvocationDefinitionSource 的实例

     查看其包含的元素得知 这个类就是用来存放资源信息的。

     因此,得出结论,我们只需要定义自己的DefaultFilterInvocationDefinitionSource ,并装配给FilterSecurityInterceptor 便可。

     而,通过查看源码得知,原来的DefaultFilterInvocationDefinitionSource 是通过构造器的两个参数来获取到资源信息的:

         public DefaultFilterInvocationDefinitionSource(UrlMatcher urlMatcher, LinkedHashMap requestMap);

     其中,requestMap 是 重中之重。

 

     因此,我们需要继承DefaultFilterInvocationDefinitionSource 类,并要安排在某个位置调用它的上述构造器。

 

     只有采取这种方式:

 

    //在继承类的构造器里调用父类构造器。

    //而继承规则规定:子类在明确的调用父类的构造器,其参数必须明确——即,值为静态变量值或者为静态方法的返回值。

    public ObejctDefinitionSourceImpl(UrlMatcher urlMatcher,

                     LinkedHashMap requestMap) {

       super(urlMatcher, requestMap);

    }

 

 

   因此,我现在采取下面方式来完成功能的实现

       (具体实现可以参看spring-security源码----HttpSecurityBeanDefinitionParser类:parseInterceptUrlsForFilterInvocationRequestMap方法):

=*==*==*==*==*==*==*==*==*==*==**==*==*==*==**==*==*==*==**==*==*==*==**==*==*==*==*=


view plain copy to clipboard print ?



  1. package security.service;  
  2.    
  3. import java.util.HashMap;  
  4. import java.util.LinkedHashMap;  
  5.    
  6. import org.springframework.security.ConfigAttributeEditor;  
  7. import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;  
  8. import org.springframework.security.intercept.web.RequestKey;  
  9. import org.springframework.security.util.AntUrlPathMatcher;  
  10. import org.springframework.security.util.UrlMatcher;  
  11.    
  12. @SuppressWarnings("unchecked")  
  13. public class ObejctDefinitionSourceImpl extends  
  14.               DefaultFilterInvocationDefinitionSource {  
  15.    
  16.        private static UrlMatcher urlMatcher;  
  17.        private static LinkedHashMap requestMap = new LinkedHashMap();  
  18.          
  19.         //采用静态初始化的方式,来准备父类构造器的参数值   
  20.        static {  
  21.               urlMatcher = new AntUrlPathMatcher();  
  22.               ConfigAttributeEditor editor = new ConfigAttributeEditor();  
  23.    
  24.               editor.setAsText("ROLE_USER");  
  25.               Object key = new RequestKey("/index.jsp*", null);  
  26.               //editor.getValue() 不能省略为:"ROLE_ADMIN",并需要经过editor这一道中转站   
  27.               requestMap.put(key, editor.getValue());  
  28.    
  29.               editor.setAsText("ROLE_ADMIN");  
  30.               key = new RequestKey("/admin*.jsp", null);  
  31.               requestMap.put(key, editor.getValue());  
  32.        }  
  33.    
  34.        public ObejctDefinitionSourceImpl(UrlMatcher urlMatcher,  
  35.                      LinkedHashMap requestMap) {  
  36.               super(urlMatcher, requestMap);  
  37.        }  
  38.    
  39.        //采用静态工厂方法来创建本实例————因为这样才能使用带参的构造器来实例化bean。   
  40.        public static ObejctDefinitionSourceImpl createObejctDefinitionSource() {  
  41.               return new ObejctDefinitionSourceImpl(urlMatcher, requestMap);  
  42.        }  
  43.    
  44. }  


package security.service; import java.util.HashMap;import java.util.LinkedHashMap; import org.springframework.security.ConfigAttributeEditor;import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;import org.springframework.security.intercept.web.RequestKey;import org.springframework.security.util.AntUrlPathMatcher;import org.springframework.security.util.UrlMatcher; @SuppressWarnings("unchecked")public class ObejctDefinitionSourceImpl extends DefaultFilterInvocationDefinitionSource { private static UrlMatcher urlMatcher; private static LinkedHashMap requestMap = new LinkedHashMap(); //采用静态初始化的方式,来准备父类构造器的参数值 static { urlMatcher = new AntUrlPathMatcher(); ConfigAttributeEditor editor = new ConfigAttributeEditor(); editor.setAsText("ROLE_USER"); Object key = new RequestKey("/index.jsp*", null); //editor.getValue() 不能省略为:"ROLE_ADMIN",并需要经过editor这一道中转站 requestMap.put(key, editor.getValue()); editor.setAsText("ROLE_ADMIN"); key = new RequestKey("/admin*.jsp", null); requestMap.put(key, editor.getValue()); } public ObejctDefinitionSourceImpl(UrlMatcher urlMatcher, LinkedHashMap requestMap) { super(urlMatcher, requestMap); } //采用静态工厂方法来创建本实例————因为这样才能使用带参的构造器来实例化bean。 public static ObejctDefinitionSourceImpl createObejctDefinitionSource() { return new ObejctDefinitionSourceImpl(urlMatcher, requestMap); } }  

 

 

=*==*==*==*==*==*==*==*==*==*==**==*==*==*==**==*==*==*==**==*==*==*==**==*==*==*==*=

接下来要解决两个问题:

   ①. 将ObejctDefinitionSourceImpl 装备给FilterSecurityInterceptor,覆盖其早先的值。

   ②. 因为要为FilterSecurityInterceptor装配新值,那么势必要重新配置它,相当于是一个新的bean。要如何取代流程中以往的FilterSecurityInterceptor实例,而将现在的新配的这个bean实例插入到spring-security流程中呢?

 

方案为:

    <b:bean id="obejctDefinitionSource" class="security.service.ObejctDefinitionSourceImpl"

             factory-method="createObejctDefinitionSource" />

     

    <b:bean class="org.springframework.security.intercept.web.FilterSecurityInterceptor"

             autowire="byType">

          <!-- 使 NEXT 指向原来那个实例的 Filter,指向现在新定义的 FilterSecurityInterceptor 实例,

              而这个实例自然会指向下一个Filter,原先的实例相当于被架空-->

        <custom-filter before="FILTER_SECURITY_INTERCEPTOR" />

 

         <!-- 装配自定义的资源信息容器类 -->

        <b:property name="objectDefinitionSource" ref="obejctDefinitionSource" />

    </b:bean>

  

特别注意:配置 FilterSecurityInterceptor 时的 autowire="byType" 不能少。因为它还有其它很多必要属性,索性ByType让它自己装配得了。

 

 

=*==*==*==*==*==*==*==*==*==*==*==*==*==*==**==*==*==*==**==*==*==*==**==*==*==*==*=

另外还可以使用BeanFactory

  Spring 中有两种类型的 Bean, 一种是普通Bean, 另一种是工厂 Bean, 即 FactoryBean.

  工厂 Bean 跟普通Bean不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean 的 getObject 方法所返回的对象。

 

例:

  public class CustomerFacroty implements FactoryBean {

 

       // 此方法返回的对象,为bean实例化成功后spring容器最终存放的对象

       @Override

       public Object getObject() throws Exception {

              return new Customer("wjh", 21);

       }

 

       // 创建的对象的类型

       @Override

       public Class getObjectType() {

              return Customer.class;

       }

  

       // 是否采用单例模式来管理bean

       @Override

       public boolean isSingleton() {

              return false;

       }

  }

 

 

<b:bean id="customerFacroty" class="junit.test.CustomerFacroty" />

这样配置后,spring存放的标识为customerFacroty的bean实际上是 new Customer("wjh", 21) 这个对象, 而不是 CustomerFacroty 。

 

 

代码:

=*==*==*==*==*==*==*==*==*==*==*==  


view plain copy to clipboard print ?



  1. package security.service;  
  2.    
  3. import java.util.LinkedHashMap;  
  4.    
  5. import org.springframework.beans.factory.FactoryBean;  
  6. import org.springframework.security.ConfigAttributeEditor;  
  7. import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;  
  8. import org.springframework.security.intercept.web.RequestKey;  
  9. import org.springframework.security.util.AntUrlPathMatcher;  
  10. import org.springframework.security.util.UrlMatcher;  
  11.    
  12. @SuppressWarnings("unchecked")  
  13. public class ObejctDefinitionSourceImpl2 implements FactoryBean {  
  14.    
  15.         // 一目了然,之所以采用这种方式,只是为了能调用非静态方法来获取 DefaultFilterInvocationDefinitionSource 的两个参数   
  16.        @Override  
  17.        public Object getObject() throws Exception {  
  18.               UrlMatcher urlMatcher = new AntUrlPathMatcher();  
  19.               LinkedHashMap requestMap = getRequestMap();  
  20.               return new DefaultFilterInvocationDefinitionSource(urlMatcher, requestMap);  
  21.        }  
  22.    
  23.        private LinkedHashMap getRequestMap() {  
  24.               LinkedHashMap requestMap = new LinkedHashMap();  
  25.               ConfigAttributeEditor editor = new ConfigAttributeEditor();  
  26.                
  27.               editor.setAsText("ROLE_USER");  
  28.               Object key = new RequestKey("/index.jsp*", null);  
  29.               requestMap.put(key, editor.getValue());  
  30.    
  31.               editor.setAsText("ROLE_ADMIN");  
  32.               key = new RequestKey("/admin*.jsp", null);  
  33.               requestMap.put(key, editor.getValue());  
  34.                
  35.               return requestMap;  
  36.        }  
  37.    
  38.        @Override  
  39.        public Class getObjectType() {  
  40.               return DefaultFilterInvocationDefinitionSource.class;  
  41.        }  
  42.    
  43.        @Override  
  44.        public boolean isSingleton() {  
  45.               return true;  
  46.        }  
  47.    
  48. }  


package security.service; import java.util.LinkedHashMap; import org.springframework.beans.factory.FactoryBean;import org.springframework.security.ConfigAttributeEditor;import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;import org.springframework.security.intercept.web.RequestKey;import org.springframework.security.util.AntUrlPathMatcher;import org.springframework.security.util.UrlMatcher; @SuppressWarnings("unchecked")public class ObejctDefinitionSourceImpl2 implements FactoryBean { // 一目了然,之所以采用这种方式,只是为了能调用非静态方法来获取 DefaultFilterInvocationDefinitionSource 的两个参数 @Override public Object getObject() throws Exception { UrlMatcher urlMatcher = new AntUrlPathMatcher(); LinkedHashMap requestMap = getRequestMap(); return new DefaultFilterInvocationDefinitionSource(urlMatcher, requestMap); } private LinkedHashMap getRequestMap() { LinkedHashMap requestMap = new LinkedHashMap(); ConfigAttributeEditor editor = new ConfigAttributeEditor(); editor.setAsText("ROLE_USER"); Object key = new RequestKey("/index.jsp*", null); requestMap.put(key, editor.getValue()); editor.setAsText("ROLE_ADMIN"); key = new RequestKey("/admin*.jsp", null); requestMap.put(key, editor.getValue()); return requestMap; } @Override public Class getObjectType() { return DefaultFilterInvocationDefinitionSource.class; } @Override public boolean isSingleton() { return true; } } 

=*==*==*==*==*==*==*==*==*==*==*==

装配方式相同:

      <b:bean id="obejctDefinitionSource2" class="security.service.ObejctDefinitionSourceImpl2" />

     

      <b:bean class="org.springframework.security.intercept.web.FilterSecurityInterceptor"

             autowire="byType">

             <custom-filter before="FILTER_SECURITY_INTERCEPTOR" />

             <b:property name="objectDefinitionSource" ref="obejctDefinitionSource2" />

      </b:bean> 

 

 

=*==*==*==*==*==*==*==*==*==*==*==

个人觉得第一种更好。

第二种没有必要。

如果真的只是为了调用方法来获取构造器参数,可以写成这样子。

前提:继承

  public ObejctDefinitionSourceImpl(UrlMatcher urlMatcher,

              LinkedHashMap requestMap) {

       super(getMatcher(), getRequestMap());

  }

 

  public static UrlMatcher getMatcher(){

       return null;

  }

      

  public static LinkedHashMap getRequestMap() {

       return null;

  }

 

 

  

********************************

在上例的基础上,从数据库获取资源信息

 

数据库表结构 :

 

CREATE TABLE `resource` (

  `Id`         int(11) ,

  `value`      varchar(50)

)

 

CREATE TABLE `resource_role` (

  `resource_id`         int(11) ,

  `role_id`             int(11),

)

create table role(

    id bigint,

    name varchar(50),

    descn varchar(200)

);

     ----------  与上例结合的数据库表结构

 

 

这种情况下,其实上例中的第一种方式不太好使:

因为要使用到数据库连接对象,而一般情况下,是通过注入DAO来完成数据库间接访问的。

本例中通过注入SimpleJdbcTemplate来对数据库进行访问。

 

而,因为第一种方式需要在初始化类时,便需要调用(最后一种方式,也需要在构造器中调用静态方法)。但,此时没有办法在之前就注入SimpleJdbcTemplate这个bean。

使用<property>方式注入的对象,需要在对象实例化成功之后(构造器调用之后)才开始注入。

构造器方式的注入此例中更不适合,因为,对于此对象,我们已经被限定了一种构造器的使用。。

 

 

我晕 , 突然想到可以这样。

在第一种方式的基础至少这样写:

static {

       ApplicationContext acx  = new ClassPathXmlApplicationContext("applicationContext*.xml");

       SimpleJdbcTemplate jdbcTemplate =  (SimpleJdbcTemplate) acx.getBean("jdbcTemplate");

}

这样不就可以获取到数据库访问对象了么,我一直纠结于以注入的方式来获取它。却忘了还可以直接getBean。

 

 

所以我傻傻的采用了实例化工厂bean方法的方式来完成需求 ,代码:

=*==*==*==*==*==*==*==*==*==*==*==*==*==*==**==*==*==*==**==*==*==*==**==*==*==*==*=

 


view plain copy to clipboard print ?



  1. package security.service;  
  2.    
  3. import java.util.LinkedHashMap;  
  4. import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;  
  5. import org.springframework.security.util.UrlMatcher;  
  6.    
  7. @SuppressWarnings("unchecked")  
  8. public class ObejctDefinitionSourceImpl extends  
  9.               DefaultFilterInvocationDefinitionSource {  
  10.    
  11.         
  12.        public ObejctDefinitionSourceImpl(UrlMatcher urlMatcher,  
  13.                      LinkedHashMap requestMap) {  
  14.                super(urlMatcher, requestMap);  
  15.        }  
  16. }  


package security.service; import java.util.LinkedHashMap;import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;import org.springframework.security.util.UrlMatcher; @SuppressWarnings("unchecked")public class ObejctDefinitionSourceImpl extends DefaultFilterInvocationDefinitionSource { public ObejctDefinitionSourceImpl(UrlMatcher urlMatcher, LinkedHashMap requestMap) { super(urlMatcher, requestMap); }} 

 

除此之外,为了能成功注入数据库访问对象,我编写了一个FactoryBean :

 


view plain copy to clipboard print ?



  1. package security.service;  
  2.    
  3. import java.util.HashMap;  
  4. import java.util.LinkedHashMap;  
  5. import java.util.List;  
  6. import java.util.Map;  
  7. import java.util.Set;  
  8.    
  9. import org.springframework.context.ApplicationContext;  
  10. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  11. import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;  
  12. import org.springframework.security.ConfigAttributeEditor;  
  13. import org.springframework.security.intercept.web.RequestKey;  
  14. import org.springframework.security.util.AntUrlPathMatcher;  
  15. import org.springframework.security.util.UrlMatcher;  
  16.    
  17. @SuppressWarnings("unchecked")  
  18. public class ObjectDefinitionSourceFactory {  
  19.         
  20.        private static SimpleJdbcTemplate jdbcTemplate;  
  21.        private static ObejctDefinitionSourceImpl obejctDefinitionSource = null;  
  22.         
  23.        public ObjectDefinitionSourceFactory(SimpleJdbcTemplate jdbcTemplate) {  
  24.               this.jdbcTemplate = jdbcTemplate;  
  25.        }  
  26.         
  27.        public ObejctDefinitionSourceImpl createObejctDefinitionSource() {  
  28.                
  29.               return getObejctDefinitionSource();  
  30.        }  
  31.    
  32.        public static ObejctDefinitionSourceImpl getObejctDefinitionSource() {  
  33.                
  34.               if(obejctDefinitionSource != null) {  
  35.                      return obejctDefinitionSource;  
  36.               }  
  37.               LinkedHashMap requestMap = getRequestMap();  
  38.               UrlMatcher urlMatcher = new AntUrlPathMatcher();  
  39.                
  40.               obejctDefinitionSource = new ObejctDefinitionSourceImpl(urlMatcher, requestMap);  
  41.                
  42.               return obejctDefinitionSource;  
  43.        }  
  44.    
  45.        private static LinkedHashMap getRequestMap() {  
  46.               LinkedHashMap requestMap = new LinkedHashMap();  
  47.                
  48.               Map<String, String> resources = getResourcesForQueryMap();  
  49.                
  50.               ConfigAttributeEditor editor = new ConfigAttributeEditor();  
  51.               Set<Map.Entry<String, String>> set = resources.entrySet();  
  52.               for (Map.Entry<String, String> resource : set) {  
  53.                      // 如果权限为空,则使用默认值,使所有人都可以访问。   
  54.                      String role = "IS_AUTHENTICATED_ANONYMOUSLY";  
  55.                      if(resource.getValue() != null) {  
  56.                             role = resource.getValue();  
  57.                      }  
  58.    
  59.                      editor.setAsText(role);  
  60.                      Object key = new RequestKey(resource.getKey(), null);  
  61.                      requestMap.put(key, editor.getValue());  
  62.                       
  63.                          // 如果权限为空,任何人都不能访问   
  64.                      /*Object key = new RequestKey(resource.getKey(), null); 
  65.   
  66.                      String role = resource.getValue(); 
  67.                      if(role == null) { 
  68.                             requestMap.put(key, ConfigAttributeDefinition.NO_ATTRIBUTES); 
  69.                      } else { 
  70.                             editor.setAsText(role); 
  71.                             requestMap.put(key, editor.getValue()); 
  72.                      }*/  
  73.               }  
  74.                
  75.               return requestMap;  
  76.        }  
  77.    
  78.        //注:sql一般都要写成左链接(left join),因为,我们不希望如果一个资源没有对应任何权限的话,便抛弃它。   
  79.        // 而是希望,如果一个资源部对应任何权限的时候,任何人都可以访问它或者任何人都不能访问它。   
  80.        private static Map<String, String> getResourcesForQueryMap() {  
  81.               String sql = "select value, name " + "from resource r "  
  82.                             + "left join resource_role rr " + "on r.id = rr.resource_id "  
  83.                             + "left join role ro " + "on ro.id = rr.role_id";  
  84.               List<Map<String, Object>> resourcesArole = jdbcTemplate.queryForList(sql);  
  85.                
  86.               Map<String, String> resources = new HashMap<String, String>();  
  87.                
  88.               for (Map<String, Object> map : resourcesArole) {  
  89.                      String path = (String) map.get("value");  
  90.                      String role = (String) map.get("name");  
  91.                       
  92.                      //以逗号隔开多个权限对应一个URL资源   
  93.                      if (resources.containsKey(path)) {  
  94.                             role = resources.get(path) + "," + role;  
  95.                      }  
  96.                       
  97.                      resources.put(path, role);  
  98.               }  
  99.                
  100.               return resources;  
  101.        }  
  102.         
  103. }  


package security.service; import java.util.HashMap;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import java.util.Set; import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;import org.springframework.security.ConfigAttributeEditor;import org.springframework.security.intercept.web.RequestKey;import org.springframework.security.util.AntUrlPathMatcher;import org.springframework.security.util.UrlMatcher; @SuppressWarnings("unchecked")public class ObjectDefinitionSourceFactory { private static SimpleJdbcTemplate jdbcTemplate; private static ObejctDefinitionSourceImpl obejctDefinitionSource = null; public ObjectDefinitionSourceFactory(SimpleJdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public ObejctDefinitionSourceImpl createObejctDefinitionSource() { return getObejctDefinitionSource(); } public static ObejctDefinitionSourceImpl getObejctDefinitionSource() { if(obejctDefinitionSource != null) { return obejctDefinitionSource; } LinkedHashMap requestMap = getRequestMap(); UrlMatcher urlMatcher = new AntUrlPathMatcher(); obejctDefinitionSource = new ObejctDefinitionSourceImpl(urlMatcher, requestMap); return obejctDefinitionSource; } private static LinkedHashMap getRequestMap() { LinkedHashMap requestMap = new LinkedHashMap(); Map<String, String> resources = getResourcesForQueryMap(); ConfigAttributeEditor editor = new ConfigAttributeEditor(); Set<Map.Entry<String, String>> set = resources.entrySet(); for (Map.Entry<String, String> resource : set) { // 如果权限为空,则使用默认值,使所有人都可以访问。 String role = "IS_AUTHENTICATED_ANONYMOUSLY"; if(resource.getValue() != null) { role = resource.getValue(); } editor.setAsText(role); Object key = new RequestKey(resource.getKey(), null); requestMap.put(key, editor.getValue()); // 如果权限为空,任何人都不能访问 /*Object key = new RequestKey(resource.getKey(), null); String role = resource.getValue(); if(role == null) { requestMap.put(key, ConfigAttributeDefinition.NO_ATTRIBUTES); } else { editor.setAsText(role); requestMap.put(key, editor.getValue()); }*/ } return requestMap; } //注:sql一般都要写成左链接(left join),因为,我们不希望如果一个资源没有对应任何权限的话,便抛弃它。 // 而是希望,如果一个资源部对应任何权限的时候,任何人都可以访问它或者任何人都不能访问它。 private static Map<String, String> getResourcesForQueryMap() { String sql = "select value, name " + "from resource r " + "left join resource_role rr " + "on r.id = rr.resource_id " + "left join role ro " + "on ro.id = rr.role_id"; List<Map<String, Object>> resourcesArole = jdbcTemplate.queryForList(sql); Map<String, String> resources = new HashMap<String, String>(); for (Map<String, Object> map : resourcesArole) { String path = (String) map.get("value"); String role = (String) map.get("name"); //以逗号隔开多个权限对应一个URL资源 if (resources.containsKey(path)) { role = resources.get(path) + "," + role; } resources.put(path, role); } return resources; } } 

 

=*==*==*==*==*==*==*==*=


view plain copy to clipboard print ?



  1. <b:bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">  
  2.        <b:constructor-arg ref="dataSource" />  
  3. </b:bean>  
  4.    
  5. <b:bean id="objectDefinitionSourceFactory" class="security.service.ObjectDefinitionSourceFactory">  
  6.        <!-- 构造器的方式加入数据库访问对象 -->  
  7.        <b:constructor-arg ref="jdbcTemplate" />  
  8. </b:bean>       
  9.         
  10. <b:bean id="obejctDefinitionSource" class="security.service.ObejctDefinitionSourceImpl"  
  11.        factory-method="createObejctDefinitionSource" factory-bean="objectDefinitionSourceFactory"/>  
  12.    
  13. <b:bean class="org.springframework.security.intercept.web.FilterSecurityInterceptor"  
  14.        autowire="byType">  
  15.        <custom-filter before="FILTER_SECURITY_INTERCEPTOR" />  
  16.        <b:property name="objectDefinitionSource" ref="obejctDefinitionSource" />  
  17. </b:bean>  


<b:bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate"> <b:constructor-arg ref="dataSource" /></b:bean> <b:bean id="objectDefinitionSourceFactory" class="security.service.ObjectDefinitionSourceFactory"> <!-- 构造器的方式加入数据库访问对象 --> <b:constructor-arg ref="jdbcTemplate" /></b:bean> <b:bean id="obejctDefinitionSource" class="security.service.ObejctDefinitionSourceImpl" factory-method="createObejctDefinitionSource" factory-bean="objectDefinitionSourceFactory"/> <b:bean class="org.springframework.security.intercept.web.FilterSecurityInterceptor" autowire="byType"> <custom-filter before="FILTER_SECURITY_INTERCEPTOR" /> <b:property name="objectDefinitionSource" ref="obejctDefinitionSource" /></b:bean> 

 

=*==*==*==*==*==*==*==*=

另外以  FactoryBean  较之上面这种更简单,清晰。不做记录。

 

********************

MD5加密

<sec:authentication-provider user-service-ref="userDetailsService">

       <!-- 启用密码的 md5 加密 -->

       <sec:password-encoder hash="md5">

              <sec:salt-source user-property="username"/>

       </sec:password-encoder>             

</sec:authentication-provider>

 

配置之后,它会将登陆时输入的密码进行MD5加密之后,再进行验证。

 

*********************

盐值加密,因为如果密码设置得过于简单,虽然进行了md5加密,但还是有可能被反推出原密码。虽然很难~~~

于是想出一个办法,对密码进行盐值。并且盐值也有一定的迷惑性。

盐值的原理非常简单,就是先把密码和盐值指定的内容合并在一起,再使用md5对合并后的内容进行演算" 中的 "先把密码和盐值指定的内容合并在一起" 时什么意思, 及密码和盐值怎样放在一起?

 

spring-security中使用盐值的方式进行MD5加密的配置方式为:

<sec:password-encoder hash="md5">

       <sec:salt-source user-property="username"/> <!-- 表示以用户名做为盐值 -->

</sec:password-encoder>

 

但有个问题文档中没有予以说明,就是 spring-security 中盐值的方式是采用哪种方式:是加在密码的前边?后边?中间?或者打散了随机加?

 

通过阅读 org.springframework.security.providers.encoding.BasePasswordEncoder 类的 mergePasswordAndSalt 方法得知 "先把密码和盐值指定的内容合并在一起" 是通过以下代码完成的: return password + "{" + salt.toString() + "}";

 

因此,在录入新密码的时候,进行md5算法加密的时候,需要以同样的方式进行盐值加密。否则会出现用户输入的密码永远无法匹配上数据库密码的

现象。

 

 

      

********************

页面上访问用户信息。

<security:authentication property="name"></security:authentication>

Authentication接口里面定义的所有getXXX方法的返回值对象及其嵌套值,都可以这样访问到

例:

  <security:authentication property="details"></security:authentication>

 

 

 

****************

管理session

同一个账号只能登陆一次

先在web.xml中配置:

<listener>

    <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>

</listener>

 

然后在spring配置文件配置:

<sec:concurrent-session-control/>

默认的是后登陆的将前面的踢出

也可以改变:

<sec:concurrent-session-control exception-if-maximum-exceeded="true"/>

这时候是前面登陆了,后面不允许登上去。