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

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

一、引言

因项目需要最近研究了下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 wzhang16  *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             // 用户所拥有的权限authentication43             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 set102      */103     public void setAccess(Integer access) {104         this.access = access;105     }106     /**107      * @return the role108      */109     public RoleBean getRole() {110         return role;111     }112     /**113      * @param role the role to set114      */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_ADMIN48             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_USER57             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 wzhang23  *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 wzhang22  *23  */24 public class CustomFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {25     private static final Logger logger = Logger26             .getLogger(CustomFilterSecurityInterceptor.class);27     private FilterInvocationSecurityMetadataSource securityMetadataSource;28 29     /**30      * Method that is actually called by the filter chain. Simply delegates to31      * the {@link #invoke(FilterInvocation)} method.32      * 33      * @param request34      *            the servlet request35      * @param response36      *            the servlet response37      * @param chain38      *            the filter chain39      * 40      * @throws IOException41      *             if the filter chain fails42      * @throws ServletException43      *             if the filter chain fails44      */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 stub83 84     }85 86     public void init(FilterConfig arg0) throws ServletException {87         // TODO Auto-generated method stub88 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.xml25         </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="http://www.mamicode.com/false" />46         <beans:property name="decisionVoters">47             <beans:list>48                 <beans:bean class="org.springframework.security.access.vote.RoleVoter" />49                 <beans:bean50                     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     <bean16         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 @Controller11 public class HelloController {12 13     protected static Logger logger = Logger.getLogger("controller");14     15     @RequestMapping(value = http://www.mamicode.com/{"/", "/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 = "http://www.mamicode.com/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 = "http://www.mamicode.com/login**", method = RequestMethod.GET)40     public ModelAndView loginPage(41         @RequestParam(value = "http://www.mamicode.com/error", required = false) String error,42         @RequestParam(value = "http://www.mamicode.com/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 = "http://www.mamicode.com/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 = "http://www.mamicode.com/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