代码改变世界

JavaEE学习之Spring Security3.x——模拟数据库实现用户,权限,资源的管理

2014-08-10 23:24  ps_zw  阅读(3824)  评论(1编辑  收藏  举报

一、引言

因项目需要最近研究了下Spring Security3.x,并模拟数据库实现用户,权限,资源的管理。

二、准备

1.了解一些Spring MVC相关知识;

2.了解一些AOP相关知识;

3.了解Spring;

4.了解Maven,并安装。

三、实现步骤

本示例中使用的版本是Spring Security3.2.2.通过数据库实现Spring Security认证授权大致需要以下几个步骤:

1.新建maven web project(因为本示例使用的是maven来构建的),项目结构如下,忽略红叉叉:

2.在pom文件中添加SpringMVC,Spring Security3.2等依赖的相关jar包,代码如下:

 1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 2   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 3   <modelVersion>4.0.0</modelVersion>
 4   <groupId>com.wzhang</groupId>
 5   <artifactId>spring-mvc-security-helloworld</artifactId>
 6   <packaging>war</packaging>
 7   <version>1.0</version>
 8   <name>spring-mvc-security-helloworld Maven Webapp</name>
 9   <url>http://maven.apache.org</url>
10 <dependencies>
11         <dependency>
12             <groupId>junit</groupId>
13             <artifactId>junit</artifactId>
14             <version>3.8.1</version>
15         </dependency>
16         <dependency>
17             <groupId>commons-logging</groupId>
18             <artifactId>commons-logging</artifactId>
19             <version>1.1.1</version>
20             <scope>compile</scope>
21             <optional>true</optional>
22         </dependency>
23         <dependency>
24             <groupId>org.springframework</groupId>
25             <artifactId>spring-web</artifactId>
26             <version>3.2.3.RELEASE</version>
27         </dependency>
28         <dependency>
29             <groupId>org.springframework</groupId>
30             <artifactId>spring-security-core</artifactId>
31             <version>3.2.2.RELEASE</version>
32         </dependency>
33         <dependency>
34             <groupId>org.springframework</groupId>
35             <artifactId>spring-beans</artifactId>
36             <version>3.2.3.RELEASE</version>
37         </dependency>
38         <dependency>
39             <groupId>org.springframework</groupId>
40             <artifactId>spring-context</artifactId>
41             <version>3.2.3.RELEASE</version>
42         </dependency>
43         <dependency>
44             <groupId>org.springframework.security</groupId>
45             <artifactId>spring-security-config</artifactId>
46             <version>3.2.2.RELEASE</version>
47             <scope>compile</scope>
48         </dependency>
49         <dependency>
50             <groupId>org.springframework</groupId>
51             <artifactId>spring-webmvc</artifactId>
52             <version>3.2.3.RELEASE</version>
53         </dependency>
54         <dependency>
55             <groupId>org.springframework.security</groupId>
56             <artifactId>spring-security-web</artifactId>
57             <version>3.2.2.RELEASE</version>
58             <scope>compile</scope>
59         </dependency>
60         <dependency>
61             <groupId>org.springframework</groupId>
62             <artifactId>spring-core</artifactId>
63             <version>3.2.3.RELEASE</version>
64             <scope>compile</scope>
65             <exclusions>
66                 <exclusion>
67                     <artifactId>commons-logging</artifactId>
68                     <groupId>commons-logging</groupId>
69                 </exclusion>
70             </exclusions>
71         </dependency>
72         <dependency>
73             <groupId>jstl</groupId>
74             <artifactId>jstl</artifactId>
75             <version>1.2</version>
76         </dependency>
77         <dependency>
78             <groupId>log4j</groupId>
79             <artifactId>log4j</artifactId>
80             <version>1.2.17</version>
81         </dependency>
82         <dependency>
83             <groupId>javax.servlet</groupId>
84             <artifactId>servlet-api</artifactId>
85             <version>2.5</version>
86         </dependency>
87 </dependencies>
88   <build>
89     <finalName>spring-mvc-security-helloworld</finalName>
90     
91   </build>
92 </project>
View Code

3.实现FilterInvocationSecurityMetadataSource接口,完成从数据库中获取资源权限的关系;

  a.为了模拟数据库我这里定义了几个类/接口:ResourceDao.java(访问数据库资源的接口),ResourceDaoImpl.java(访问数据库资源的实现类).代码如下:  

package com.wzhang.dao;

import java.util.Map;

public interface ResourceDao {
    /**
     * 获取资源权限列表
     * @return
     */
    Map<String,String> getResources();
}

/********************以下是ResourceDao实现类 ************************/

package com.wzhang.dao.impl;

import java.util.HashMap;
import java.util.Map;

import com.wzhang.common.RoleConstants;
import com.wzhang.dao.ResourceDao;

public class ResourceDaoImpl implements ResourceDao {

    /**
     * 获取所有资源权限映射
     * key-URL
     * value-Role
     */
    public Map<String, String> getResources() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("/admin**", RoleConstants.ROLE_ADMIN);
        map.put("/index**", RoleConstants.ROLE_USER);
        return map;
    }

}
View Code

  b.自定义FilterInvocationSecurityMetadataSource接口实现类,利用刚刚的接口和实现类,实现从数据库获取资源权限的关系,代码如下:

  1 package com.wzhang.security.service;
  2 
  3 import java.util.ArrayList;
  4 import java.util.Collection;
  5 import java.util.HashMap;
  6 import java.util.Iterator;
  7 import java.util.Map;
  8 import java.util.Map.Entry;
  9 
 10 import org.apache.log4j.Logger;
 11 import org.springframework.security.access.ConfigAttribute;
 12 import org.springframework.security.access.SecurityConfig;
 13 import org.springframework.security.web.FilterInvocation;
 14 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
 15 import org.springframework.util.AntPathMatcher;
 16 import org.springframework.util.PathMatcher;
 17 
 18 import com.wzhang.dao.ResourceDao;
 19 import com.wzhang.dao.impl.ResourceDaoImpl;
 20 
 21 /**
 22  * 资源源数据定义,即定义某一资源可以被哪些角色访问
 23  * @author wzhang
 24  *
 25  */
 26 public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource  {
 27 
 28     private static final Logger logger = Logger
 29             .getLogger(CustomFilterInvocationSecurityMetadataSource .class);
 30 
 31     private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
 32     private PathMatcher pathMatcher = new AntPathMatcher();
 33     
 34     private ResourceDao resourceDao;    
 35     
 36     public CustomFilterInvocationSecurityMetadataSource(ResourceDao resourceDao ){
 37         this.resourceDao =resourceDao;
 38         resourceMap = loadResourceMatchAuthority();
 39     }
 40 
 41     public Collection<ConfigAttribute> getAllConfigAttributes() {
 42 
 43         return null;
 44     }
 45 
 46     public CustomFilterInvocationSecurityMetadataSource  () {
 47         super();
 48         this.resourceDao  = new ResourceDaoImpl();
 49         resourceMap = loadResourceMatchAuthority();
 50     }
 51 
 52     /**
 53      * 加载资源与权限的映射关系
 54      * 
 55      * @return
 56      */
 57     private Map<String, Collection<ConfigAttribute>> loadResourceMatchAuthority() {
 58 
 59         Map<String, Collection<ConfigAttribute>> map = new HashMap<String, Collection<ConfigAttribute>>();
 60 
 61         // 获取资源权限映射key:url,value:role
 62         Map<String, String> configs = resourceDao.getResources();
 63         for (Entry<String, String> entry : configs.entrySet()) {
 64             Collection<ConfigAttribute> list = new ArrayList<ConfigAttribute>();
 65 
 66             String[] vals = entry.getValue().split(",");
 67             for (String val : vals) {
 68                 ConfigAttribute config = new SecurityConfig(val);
 69                 list.add(config);
 70             }
 71             map.put(entry.getKey(), list);
 72         }
 73 
 74         return map;
 75 
 76     }
 77 
 78     public Collection<ConfigAttribute> getAttributes(Object object)
 79             throws IllegalArgumentException {
 80         String url = ((FilterInvocation) object).getRequestUrl();
 81 
 82         System.out.println("requestUrl is " + url);
 83         logger.info("requestUrl is " + url);
 84         
 85         if (resourceMap == null) {
 86             loadResourceMatchAuthority();
 87         }
 88         //比较url是否存在
 89         Iterator<String> ite = resourceMap.keySet().iterator();
 90         while (ite.hasNext()) {
 91             String resURL = ite.next();
 92             if (pathMatcher.match(resURL,url)) {
 93                 return resourceMap.get(resURL);
 94             }
 95         }
 96         return resourceMap.get(url);
 97     }
 98 
 99     public boolean supports(Class<?> clazz) {
100         return true;
101     }
102 }
View Code

4.实现AccessDecisionManager接口,裁定当前用户对应权限authentication是否包含所请求资源所拥有的权限;

  当用户访问某一资源时,会被AccessDecisionManager拦截,并调用decide(...)方法,用以判断当前用户是否有权限访问该资源,如果没有则抛出异常。代码如下:

 1 package com.wzhang.security.service;
 2 
 3 import java.util.Collection;
 4 import java.util.Iterator;
 5 
 6 import org.springframework.security.access.AccessDecisionManager;
 7 import org.springframework.security.access.AccessDeniedException;
 8 import org.springframework.security.access.ConfigAttribute;
 9 import org.springframework.security.authentication.InsufficientAuthenticationException;
10 import org.springframework.security.core.Authentication;
11 import org.springframework.security.core.GrantedAuthority;
12 
13 /**
14  * 自定义访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 
15  * @author wzhang
16  *
17  */
18 public class CustomAccessDecisionManager implements AccessDecisionManager {
19 
20     /**
21      * 裁定当前用户对应权限authentication是否包含所请求资源所拥有的权限 如果成立 则通过裁定 否则发生异常
22      */
23     public void decide(Authentication authentication, Object object,
24             Collection<ConfigAttribute> configAttributes)
25             throws AccessDeniedException, InsufficientAuthenticationException {
26         
27         if (configAttributes == null) {
28             return;
29         }
30 
31         // 所请求的资源拥有的权限(一个资源对多个权限)
32         Iterator<ConfigAttribute> iterator = configAttributes.iterator();
33         
34         while (iterator.hasNext()) {
35             ConfigAttribute configAttribute = iterator.next();
36             
37             // 访问所请求资源所需要的权限
38             String needPermission = configAttribute.getAttribute();
39             
40             System.out.println("needPermission is " + needPermission);
41             
42             // 用户所拥有的权限authentication
43             for (GrantedAuthority ga : authentication.getAuthorities()) {
44                 if (needPermission.equals(ga.getAuthority())) {
45                     return;
46                 }
47             }
48         }
49 
50         // 没有权限
51         throw new AccessDeniedException(" No Access Dendied ");
52 
53     }
54 
55     public boolean supports(ConfigAttribute configAttribute) {
56         return true;
57     }
58 
59     public boolean supports(Class<?> clazz) {
60         return true;
61     }
62 
63 }
View Code

5.实现UserDetailsService接口,完成从数据库获取用户-权限的关系。

  a.为了获取用户,以及用户的权限,我这里先定义了两个bean:UserBean和RoleBean,代码如下:

  1 /************RoleBean定义***********/
  2 package com.wzhang.domain;
  3 
  4 public class RoleBean {
  5     private int roleId;
  6     private String roleName;
  7     private String roleDesc;
  8     
  9     
 10     public RoleBean() {
 11         super();
 12         // TODO Auto-generated constructor stub
 13     }
 14     public RoleBean(int roleId, String roleName, String roleDesc) {
 15         super();
 16         this.roleId = roleId;
 17         this.roleName = roleName;
 18         this.roleDesc = roleDesc;
 19     }
 20     /**
 21      * @return the roleId
 22      */
 23     public int getRoleId() {
 24         return roleId;
 25     }
 26     /**
 27      * @param roleId the roleId to set
 28      */
 29     public void setRoleId(int roleId) {
 30         this.roleId = roleId;
 31     }
 32     /**
 33      * @return the roleName
 34      */
 35     public String getRoleName() {
 36         return roleName;
 37     }
 38     /**
 39      * @param roleName the roleName to set
 40      */
 41     public void setRoleName(String roleName) {
 42         this.roleName = roleName;
 43     }
 44     /**
 45      * @return the roleDesc
 46      */
 47     public String getRoleDesc() {
 48         return roleDesc;
 49     }
 50     /**
 51      * @param roleDesc the roleDesc to set
 52      */
 53     public void setRoleDesc(String roleDesc) {
 54         this.roleDesc = roleDesc;
 55     }
 56 }
 57 
 58 
 59 /**************UserBean定义*****************/
 60 
 61 
 62 package com.wzhang.domain;
 63 
 64 public class UserBean {
 65     private String userName;
 66     private String password;
 67     private Integer access;
 68     private RoleBean role;
 69     
 70     /**
 71      * @return the userName
 72      */
 73     public String getUserName() {
 74         return userName;
 75     }
 76     /**
 77      * @param userName the userName to set
 78      */
 79     public void setUserName(String userName) {
 80         this.userName = userName;
 81     }
 82     /**
 83      * @return the password
 84      */
 85     public String getPassword() {
 86         return password;
 87     }
 88     /**
 89      * @param password the password to set
 90      */
 91     public void setPassword(String password) {
 92         this.password = password;
 93     }
 94     /**
 95      * @return the access
 96      */
 97     public Integer getAccess() {
 98         return access;
 99     }
100     /**
101      * @param access the access to set
102      */
103     public void setAccess(Integer access) {
104         this.access = access;
105     }
106     /**
107      * @return the role
108      */
109     public RoleBean getRole() {
110         return role;
111     }
112     /**
113      * @param role the role to set
114      */
115     public void setRole(RoleBean role) {
116         this.role = role;
117     }
118 }
View Code

  b.接着定义了数据访问层接口和实现:UserDao.java,UserDaoImpl.java以获取用户-权限。代码如下:

 1 /*********Interface UserDao*************/
 2 
 3 package com.wzhang.dao;
 4 
 5 import com.wzhang.domain.UserBean;
 6 
 7 public interface UserDao {
 8     UserBean getUser(String userName);
 9 }
10 
11 
12 /**********实现类*******************/
13 
14 package com.wzhang.dao.impl;
15 
16 import java.util.ArrayList;
17 import java.util.List;
18 
19 import org.apache.log4j.Logger;
20 
21 import com.wzhang.common.RoleConstants;
22 import com.wzhang.dao.UserDao;
23 import com.wzhang.domain.RoleBean;
24 import com.wzhang.domain.UserBean;
25 
26 public class UserDaoImpl implements UserDao {
27     protected static Logger logger = Logger.getLogger("dao");  
28     public UserBean getUser(String userName) {
29         List<UserBean> users = internalDatabase();  
30           
31         for (UserBean ub : users) {  
32             if (ub.getUserName().equals(userName)) {  
33                 logger.debug("User found");  
34                 return ub;  
35             }  
36         }  
37         logger.error("User does not exist!");  
38         throw new RuntimeException("User does not exist!");
39     }
40     
41     
42      private List<UserBean> internalDatabase() {  
43           
44             List<UserBean> users = new ArrayList<UserBean>();  
45             UserBean user = null;  
46       
47             //创建用户admin/admin,角色ROLE_ADMIN
48             user = new UserBean();  
49             user.setUserName("admin");        
50             // "admin"经过MD5加密后  
51             user.setPassword("21232f297a57a5a743894a0e4a801fc3");
52             user.setAccess(1);
53             user.setRole(new RoleBean(1,RoleConstants.ROLE_ADMIN,""));      
54             users.add(user);  
55       
56             //创建用户user/user,角色ROLE_USER
57             user = new UserBean();  
58             user.setUserName("user");        
59             // "user"经过MD5加密后  
60             user.setPassword("ee11cbb19052e40b07aac0ca060c23ee");
61             user.setAccess(2);  
62             user.setRole(new RoleBean(2,RoleConstants.ROLE_USER,""));      
63             users.add(user);  
64       
65             return users;  
66       
67         }  
68 }
View Code

  c.代码中用到的一些常量,定在在RoleContants.kava类中了:

 1 package com.wzhang.common;
 2 
 3 /**
 4  * 角色常量
 5  * @author wzhang
 6  *
 7  */
 8 public class RoleConstants {
 9     /**
10      * 管理员角色
11      */
12     public static final String ROLE_ADMIN = "ROLE_ADMIN";
13     
14     /**
15      * 普通用户角色
16      */
17     public static final String ROLE_USER = "ROLE_USER";
18 
19 }
View Code

  d.实现UserDetailsService接口,完成用户-权限的获取(实现loadUserByUsername(...)方法):

 1 package com.wzhang.security.service;
 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.security.core.GrantedAuthority;
 9 import org.springframework.security.core.authority.SimpleGrantedAuthority;
10 import org.springframework.security.core.userdetails.User;
11 import org.springframework.security.core.userdetails.UserDetails;
12 import org.springframework.security.core.userdetails.UserDetailsService;
13 import org.springframework.security.core.userdetails.UsernameNotFoundException;
14 
15 import com.wzhang.common.RoleConstants;
16 import com.wzhang.dao.UserDao;
17 import com.wzhang.dao.impl.UserDaoImpl;
18 import com.wzhang.domain.UserBean;
19 
20 /**
21  * 自定义用户与权限的关系
22  * @author wzhang
23  *
24  */
25 public class CustomUserDetailsService implements UserDetailsService {
26     protected static Logger logger = Logger.getLogger("service");
27     private UserDao userDAO = new UserDaoImpl();
28     
29     /**
30      * 根据用户名获取用户-权限等用户信息
31      */
32     public UserDetails loadUserByUsername(String username)
33             throws UsernameNotFoundException {
34         
35         UserDetails user = null;
36         try {
37             UserBean dbUser = userDAO.getUser(username);
38             user = new User(dbUser.getUserName(), dbUser.getPassword().toLowerCase(), true, true, true, true,getAuthorities(dbUser));
39         } catch (Exception e) {
40             logger.error("Error in retrieving user");  
41             throw new UsernameNotFoundException("Error in retrieving user"); 
42         }
43         return user;
44     }
45     
46      /** 
47      * 获得访问角色权限 
48      *  
49      * @param access 
50      * @return 
51      */  
52     private Collection<GrantedAuthority> getAuthorities(UserBean dbUser) {  
53   
54         List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);  
55   
56         // 所有的用户默认拥有ROLE_USER权限  
57         logger.debug("Grant ROLE_USER to this user");  
58         authList.add(new  SimpleGrantedAuthority(RoleConstants.ROLE_USER));  
59   
60         // 如果参数access为1.则拥有ROLE_ADMIN权限  
61         if (dbUser.getRole().getRoleName().equals(RoleConstants.ROLE_ADMIN)) {  
62             logger.debug("Grant ROLE_ADMIN to this user");  
63             authList.add(new  SimpleGrantedAuthority(RoleConstants.ROLE_ADMIN));  
64         }  
65   
66         return authList;  
67     }      
68 
69 }
View Code

6.自定义Filter实现类CustomFilterSecurityInterceptor;

 1 package com.wzhang.web.filter;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.Filter;
 6 import javax.servlet.FilterChain;
 7 import javax.servlet.FilterConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 
12 import org.apache.log4j.Logger;
13 import org.springframework.security.access.SecurityMetadataSource;
14 import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
15 import org.springframework.security.access.intercept.InterceptorStatusToken;
16 import org.springframework.security.web.FilterInvocation;
17 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
18 
19 /**
20  * 配置过滤器 
21  * @author wzhang
22  *
23  */
24 public class CustomFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
25     private static final Logger logger = Logger
26             .getLogger(CustomFilterSecurityInterceptor.class);
27     private FilterInvocationSecurityMetadataSource securityMetadataSource;
28 
29     /**
30      * Method that is actually called by the filter chain. Simply delegates to
31      * the {@link #invoke(FilterInvocation)} method.
32      * 
33      * @param request
34      *            the servlet request
35      * @param response
36      *            the servlet response
37      * @param chain
38      *            the filter chain
39      * 
40      * @throws IOException
41      *             if the filter chain fails
42      * @throws ServletException
43      *             if the filter chain fails
44      */
45     public void doFilter(ServletRequest request, ServletResponse response,
46             FilterChain chain) throws IOException, ServletException {
47         FilterInvocation fi = new FilterInvocation(request, response, chain);
48         invoke(fi);
49 
50     }
51 
52     public void invoke(FilterInvocation fi) throws IOException,
53             ServletException {
54         InterceptorStatusToken token = super.beforeInvocation(fi);
55         try {
56             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
57         } catch (Exception e) {
58             logger.error(e.getStackTrace());
59         } finally {
60             super.afterInvocation(token, null);
61         }
62     }
63 
64     public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
65         return this.securityMetadataSource;
66     }
67 
68     public Class<? extends Object> getSecureObjectClass() {
69         return FilterInvocation.class;
70     }
71 
72     public SecurityMetadataSource obtainSecurityMetadataSource() {
73         return this.securityMetadataSource;
74     }
75 
76     public void setSecurityMetadataSource(
77             FilterInvocationSecurityMetadataSource newSource) {
78         this.securityMetadataSource = newSource;
79     }
80 
81     public void destroy() {
82         // TODO Auto-generated method stub
83 
84     }
85 
86     public void init(FilterConfig arg0) throws ServletException {
87         // TODO Auto-generated method stub
88 
89     }
90 
91 }
View Code

7.配置web.xml;

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 5     <display-name>Hello World-Spring MVC Security</display-name>
 6     <!-- Spring MVC -->
 7     <servlet>
 8         <servlet-name>spring</servlet-name>
 9         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
10         <load-on-startup>1</load-on-startup>
11     </servlet>
12     <servlet-mapping>
13         <servlet-name>spring</servlet-name>
14         <url-pattern>/</url-pattern>
15     </servlet-mapping>
16 
17     <listener>
18         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
19     </listener>
20 
21     <context-param>
22         <param-name>contextConfigLocation</param-name>
23         <param-value>
24             /WEB-INF/spring-security.xml
25         </param-value>
26     </context-param>
27 
28     <!-- Spring Security -->
29     <filter>
30         <filter-name>springSecurityFilterChain</filter-name>
31         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
32     </filter>
33 
34     <filter-mapping>
35         <filter-name>springSecurityFilterChain</filter-name>
36         <url-pattern>/*</url-pattern>
37     </filter-mapping>
38 </web-app>
View Code

需要注意的是:

  a.这里的servlet-name配置的是spring,后面的springmvc配置文件就得命名为spring-servlet.xml;

  b.spring-security.xml路径需要配置正确;

  c.不要忘记配置spring security的flter。

8.配置spring-security.xml;

 1 <beans:beans xmlns="http://www.springframework.org/schema/security"
 2     xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://www.springframework.org/schema/beans
 4     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 5     http://www.springframework.org/schema/security
 6     http://www.springframework.org/schema/security/spring-security-3.2.xsd">
 7 
 8 
 9     <http auto-config="true" 
10         access-denied-page="/denied">
11         <!-- <intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" />
12         <intercept-url pattern="/index" access="hasRole('ROLE_USER')" /> -->
13         <form-login login-page="/login" default-target-url="/welcome"
14             authentication-failure-url="/login?error" username-parameter="username"
15             password-parameter="password" />
16         <logout invalidate-session="true" logout-success-url="/login?logout" />
17 <custom-filter ref="customFilter" before="FILTER_SECURITY_INTERCEPTOR"/> 
18     </http>
19 
20 
21 
22     <!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
23     <authentication-manager alias="authenticationManager">
24         <authentication-provider user-service-ref="customUserDetailsService">
25             <password-encoder ref="passwordEncoder" />
26         </authentication-provider>
27     </authentication-manager>
28 
29     <!-- 密码加密方式 -->
30     <beans:bean id="passwordEncoder"
31         class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
32 
33     <!-- 自定义用户验证服务 -->
34     <beans:bean id="customUserDetailsService"
35         class="com.wzhang.security.service.CustomUserDetailsService" />
36 
37     <!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->
38     <beans:bean id="customSecurityMetadataSource"
39         class="com.wzhang.security.service.CustomFilterInvocationSecurityMetadataSource" />
40 
41     <!-- 自定义访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
42     <beans:bean id="customAccessDecisionManager"
43         class="com.wzhang.security.service.CustomAccessDecisionManager" />
44                     <!--  <beans:property name="allowIfAllAbstainDecisions"
45             value="false" />
46         <beans:property name="decisionVoters">
47             <beans:list>
48                 <beans:bean class="org.springframework.security.access.vote.RoleVoter" />
49                 <beans:bean
50                     class="org.springframework.security.access.vote.AuthenticatedVoter" />
51             </beans:list>
52         </beans:property>  -->
53     <!-- </beans:bean> -->
54 
55 
56     <beans:bean id="customFilter"
57         class="com.wzhang.web.filter.CustomFilterSecurityInterceptor">
58         <beans:property name="authenticationManager" ref="authenticationManager" />
59         <beans:property name="accessDecisionManager" ref="customAccessDecisionManager" />
60         <beans:property name="securityMetadataSource" ref="customSecurityMetadataSource" />
61     </beans:bean> 
62 
63 </beans:beans>
View Code

注意事项:

  a.<intercept-url /> 中配置的资源,已经修改为从数据库获取;

  b.需要将自定义的AccessDecisionManager,FilterInvocationSecurityMetadataSource,以及authenticationManager注入到自定义filter中。

9.实例中使用了SpringMVC框架所以还需要配置spring-servlet.xml

 1 <beans xmlns="http://www.springframework.org/schema/beans"
 2     xmlns:context="http://www.springframework.org/schema/context"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xsi:schemaLocation="
 5         http://www.springframework.org/schema/beans     
 6         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 7         http://www.springframework.org/schema/context 
 8         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 9 
10     <!-- 扫描注解组件并且自动的注入spring beans中. -->
11     <!-- 例如,扫描@Controller 和@Service下的文件.所以确保此base-package设置正确. -->
12     <context:component-scan base-package="com.wzhang.*" />
13 
14     <!-- 定义一个视图解析器。pages下的jsp文件映射到controller-->  
15     <bean
16         class="org.springframework.web.servlet.view.InternalResourceViewResolver">
17         <property name="prefix">
18             <value>/WEB-INF/pages/</value>
19         </property>
20         <property name="suffix">
21             <value>.jsp</value>
22         </property>
23     </bean>
24     
25     <bean id="resourceDao" class="com.wzhang.dao.impl.ResourceDaoImpl" ></bean>
26 </beans>
View Code

10.新建几个页面

  a.login.jsp,登录页面,任何人都能访问;

  b.admin.jsp,需要admin权限才能访问;

  c.index.jsp,user权限,admin权限均可访问;

  d.hello.jsp欢迎页面,登录后跳转,无需权限也能访问;

11.新建一个Controller,针对不同的页面定义不同的@RequestMapping;  

 1 package com.wzhang.web.controller;
 2 
 3 import org.springframework.stereotype.Controller;
 4 import org.springframework.web.bind.annotation.RequestMapping;
 5 import org.springframework.web.bind.annotation.RequestMethod;
 6 import org.springframework.web.bind.annotation.RequestParam;
 7 import org.springframework.web.servlet.ModelAndView;
 8 import org.apache.log4j.Logger;  
 9 
10 @Controller
11 public class HelloController {
12 
13     protected static Logger logger = Logger.getLogger("controller");
14     
15     @RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET)
16     public ModelAndView welcome() {
17  
18         ModelAndView model = new ModelAndView();
19         model.addObject("title", "Welcome - Spring Security Hello World");
20         model.addObject("message", "This is welcome page!");
21         model.setViewName("hello");
22         return model;
23  
24     }
25  
26     @RequestMapping(value = "/admin**", method = RequestMethod.GET)
27     public ModelAndView adminPage() {
28  
29         ModelAndView model = new ModelAndView();
30         model.addObject("title", "Admin - Spring Security Hello World");
31         model.addObject("message", "This is protected page!");
32         model.setViewName("admin");
33  
34         return model;
35  
36     }
37  
38     //Spring Security see this :
39     @RequestMapping(value = "/login**", method = RequestMethod.GET)
40     public ModelAndView loginPage(
41         @RequestParam(value = "error", required = false) String error,
42         @RequestParam(value = "logout", required = false) String logout) {
43  
44         ModelAndView model = new ModelAndView();
45         if (error != null) {
46             model.addObject("error", "Invalid username and password!");
47         }
48  
49         if (logout != null) {
50             model.addObject("msg", "You've been logged out successfully.");
51         }
52         model.setViewName("login");
53  
54         return model;
55  
56     }
57     
58     @RequestMapping(value = "/index**", method = RequestMethod.GET)
59     public ModelAndView indexPage() {
60  
61         ModelAndView model = new ModelAndView();
62         model.addObject("title", "User - Home Page");
63         model.addObject("message", "This is User access page!");
64         model.setViewName("index");
65  
66         return model;
67  
68     }
69     
70       
71     /** 
72      * 指定无访问额权限页面 
73      *  
74      * @return 
75      */  
76     @RequestMapping(value = "/denied**", method = RequestMethod.GET)  
77     public String deniedPage() {  
78   
79         logger.debug("Received request to show denied page");  
80   
81         return "denied";  
82   
83     }  
84  
85 }
View Code

 

四、测试

编译运行,

  a.输入http://localhost:8080/spring-mvc-security-helloworld/admin  将会跳转登录页面:

  

  b.输入user,user,登录后访问刚刚的网页:

  

  c.退出登录,重新使用admin,admin访问

  

从上述结果可以看出我们的自定义用户,权限,资源认证授权起到了作用。

五、总结

Spring Security3还有很多内容,比如在登录成功后的做一些处理,自定义一些provider等等。

 源码下载:spring-mvc-security-helloworld.zip