spring security学习(二)

1、配置web.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app version="2.5" 
 3     xmlns="http://java.sun.com/xml/ns/javaee" 
 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 5     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
 6     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 7   <!-- applicationContext*.xml的位置 -->
 8   <context-param>
 9   <param-name>contextConfigLocation</param-name>
10   <param-value>classpath:applicationContext*.xml</param-value>
11   </context-param>
12   <!-- 对spring进行监听 -->
13   <listener>
14   <listener-class>
15   org.springframework.web.context.ContextLoaderListener
16   </listener-class>
17   </listener>
18   <!-- springsecurity需要的filter -->
19   <filter>
20   <filter-name>springSecurityFilterChain</filter-name>
21   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
22   </filter>
23   <filter-mapping>
24   <filter-name>springSecurityFilterChain</filter-name>
25   <url-pattern>/*</url-pattern>
26   </filter-mapping>
27 </web-app>

2、配置applicationContext-security.xml(重要)

 1 <?xml version="1.0" encoding="UTF-8"?>  
 2 <beans xmlns="http://www.springframework.org/schema/beans"  
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
 4     xmlns:security="http://www.springframework.org/schema/security"  
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans   
 6             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
 7             http://www.springframework.org/schema/security   
 8             http://www.springframework.org/schema/security/spring-security-3.0.xsd"> 
 9 <security:http  auto-config="true" access-denied-page="/403.jsp">  <!-- 当访问拒绝(比如权限不够)时,会转到403.jsp -->
10 <security:intercept-url pattern="/login.jsp" filters="none"/>
11 <security:form-login login-page="/login.jsp" authentication-failure-url="/login?error=true" default-target-url="/index.jsp"/>
12 <security:logout logout-success-url="/login.jsp"/>
13 <security:http-basic/>
14 <!-- 增加一个filter,且不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前-->
15 <security:custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myFilter"/>
16 </security:http>
17 18 <!-- 一个自定义的filter,必须包含authenticationManager,accessManager,securityMetadataSource三个属性 ,所有的控制都将在这三个类中实现--> 19 <bean id="myFilter" class="com.lwh.security.filter.MyFilterSecurityInterceptor"> 20 <property name="authenticationManager" ref="authenticationManager"/> 21 <property name="accessDecisionManager" ref="myAccessDecisionManagerBean"/> 22 <property name="securityMetadataSource" ref="securityMetadataSource"/> 23 </bean> 24 25 <!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 --> 26 <security:authentication-manager alias="authenticationManager"> 27 <security:authentication-provider user-service-ref="myUserDetailService"> 28 <!-- <security:password-encoder hash="md5"/>--> 29 </security:authentication-provider> 30 </security:authentication-manager> 31 <bean id="myUserDetailService" class="com.lwh.secutity.service.MyUserDetailService"> 32 </bean> 33 <!-- 访问决策器,决定某个用户具有的角色是否有足够的权限去访问某个资源 --> 34 <bean id="myAccessDecisionManagerBean" class="com.lwh.secutity.service.MyAccessDecisionManager"> 35 </bean> 36 <!-- 资源源数据定义,即定义某一资源可以哪些角色访问 --> 37 <bean id="securityMetadataSource" class="com.lwh.secutity.service.MyInvocationSecurityMetadataSource"> 38 </bean> 39 </beans>

 3、自定义的filter的实现

 1 import java.io.IOException;
 2 import javax.servlet.Filter;
 3 import javax.servlet.FilterChain;
 4 import javax.servlet.FilterConfig;
 5 import javax.servlet.ServletException;
 6 import javax.servlet.ServletRequest;
 7 import javax.servlet.ServletResponse;
 8 import org.springframework.security.access.SecurityMetadataSource;
 9 import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
10 import org.springframework.security.access.intercept.InterceptorStatusToken;
11 import org.springframework.security.web.FilterInvocation;
12 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
13 public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor
14         implements Filter {
15     private FilterInvocationSecurityMetadataSource securityMetadataSource;
16     /**@param request
17      *        the servlet request
18      * @param response
19      *        the servlet response
20      * @param chain
21      *        the filter chain
22      * @throws IOException
23      *         if the filter chain fails
24      * @throws ServletException
25      *         if the filter chain fails
26      * 
27      */
28     public void doFilter(ServletRequest request, ServletResponse response,
29             FilterChain chain) throws IOException, ServletException {
30         FilterInvocation fi=new FilterInvocation(request, response, chain);
31         invoke(fi);
32 
33     }
34     public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
35         return securityMetadataSource;
36     }
37     @Override
38     public Class<? extends Object> getSecureObjectClass() {
39         // TODO Auto-generated method stub
40         return FilterInvocation.class;
41     }
42     public void invoke(FilterInvocation fi)throws IOException,ServletException{
43         InterceptorStatusToken token=super.beforeInvocation(fi);
44         try {
45             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
46         } catch (Exception e) {
47             // TODO: handle exception
48         }finally{
49             super.afterInvocation(token, null);
50         }
51     }
52     @Override
53     public SecurityMetadataSource obtainSecurityMetadataSource() {
54         // TODO Auto-generated method stub
55         return this.securityMetadataSource;
56     }
57     public void setSecurityMetadataSource(
58             FilterInvocationSecurityMetadataSource securityMetadataSource) {
59         this.securityMetadataSource = securityMetadataSource;
60     }
61     public void destroy() {
62         // TODO Auto-generated method stub
63 
64     }
65     public void init(FilterConfig arg0) throws ServletException {
66         // TODO Auto-generated method stub
67 
68     }
69 
70 }

最核心的代码就是invoke方法中的InterceptorStatusToken token=super.beforeInvocation(fi);这一句,即在执行doFilter之前,进行权限的检查,而具体的实现已经交给accessDecisionManager了

4、authentication-provider的实现

 1 import java.util.ArrayList;
 2 import java.util.Collection;
 3 
 4 import org.springframework.dao.DataAccessException;
 5 import org.springframework.security.core.GrantedAuthority;
 6 import org.springframework.security.core.authority.GrantedAuthorityImpl;
 7 import org.springframework.security.core.userdetails.UserDetails;
 8 import org.springframework.security.core.userdetails.User;
 9 import org.springframework.security.core.userdetails.UserDetailsService;
10 import org.springframework.security.core.userdetails.UsernameNotFoundException;
11 //该类是用于从数据库中读入用户的密码、角色信息、是否锁定、账号是否过期等
12 public class MyUserDetailService implements UserDetailsService {
13     public UserDetails loadUserByUsername(String username)
14             throws UsernameNotFoundException, DataAccessException {
15         Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>();
16         GrantedAuthorityImpl auth2=new GrantedAuthorityImpl("ROLE_ADMIN");
17         auths.add(auth2);
18         if(username.equals("louis"))
19         {
20             auths=new ArrayList<GrantedAuthority>();
21             GrantedAuthorityImpl auth1=new GrantedAuthorityImpl("ROLE_ROBIN");
22             auths.add(auth1);
23         }
24         User user=new User(username,"louis",true,true,true,true,auths);
25         return user;
26          }
27 }

这个类中,可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等

5、对于资源的访问权限定义,我们通过实现FilterInvocationSecutiryMetadataSource这个接口来初始化数据

 1 import java.util.ArrayList;
 2 import java.util.Collection;
 3 import java.util.HashMap;
 4 import java.util.Iterator;
 5 import java.util.Map;
 6 
 7 import org.springframework.security.access.ConfigAttribute;
 8 import org.springframework.security.access.SecurityConfig;
 9 import org.springframework.security.web.FilterInvocation;
10 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
11 import org.springframework.security.web.util.AntUrlPathMatcher;
12 import org.springframework.security.web.util.UrlMatcher;
13 
14 import com.sun.org.apache.bcel.internal.generic.NEW;
15 //对于资源的访问权限的定义,通过FilterInvocationSecurityMetadataSource这个接口来初始化数据
16 //假定index.jsp和i.jsp这两个页面需要Role_ADMIN角色的用户才能访问
17 //核心的地方就是提供某个资源对应的权限定义,即getAttributes方法返回的结果
18 //使用AntUrlPathMatcher这个pathmatcher来检查URL是否与资源定义匹配,也可以用正则表达式,或者实现自己在的一个matcher
19 public class MyInvocationSecurityMetadataSource implements
20         FilterInvocationSecurityMetadataSource {
21 /**
22  * 此类在初始化时,应该取得所有资源及其对应角色的定义
23  */
24     private UrlMatcher urlMatcher=new AntUrlPathMatcher();
25     private static Map<String,Collection<ConfigAttribute>> resouMap=null;
26     public MyInvocationSecurityMetadataSource(){
27         loadResourceDefine();
28     }
29     private void loadResourceDefine()
30     {
31         resouMap=new HashMap<String, Collection<ConfigAttribute>>();
32         Collection<ConfigAttribute> atts=new ArrayList<ConfigAttribute>();
33         ConfigAttribute ca=new SecurityConfig("ROLE_ADMIN");
34         atts.add(ca);
35         resouMap.put("/index.jsp", atts);
36         resouMap.put("/info.jsp", atts);
37     }
38     //According to a URL,find out permission configuration of this URL
39     public Collection<ConfigAttribute> getAttributes(Object object)
40             throws IllegalArgumentException {
41         //guess object is a URL
42         String url=((FilterInvocation)object).getRequestUrl();
43         Iterator<String> iterator=resouMap.keySet().iterator();
44         while(iterator.hasNext())
45         {
46             String resURL=iterator.next();
47             if(urlMatcher.pathMatchesUrl(resURL,url)){
48                 return resouMap.get(resURL);
49             }
50         }
51         return null;
52     }
53     public Collection<ConfigAttribute> getAllConfigAttributes() {
54         // TODO Auto-generated method stub
55         return null;
56     }
57 
58     public boolean supports(Class<?> clazz) {
59         // TODO Auto-generated method stub
60         return true;
61     }
62 
63 }

loadResourceDefine方法,假定index.jsp和info.jsp这两个资源需要ROLE_ADMIN角色的用户才能访问。

这个类中,还有一个最核心的地方,就是提供某个资源对应的权限定义,即getAttributes方法返回的结果。

注意:例子中使用的 AntUrlPathMatcher这个path matcher来检查URL是否与资源定义匹配,事实上还要用正则表达式匹配或者自己实现一个matcher

6、最后的决策

 1 import java.util.Collection;
 2 import java.util.Iterator;
 3 
 4 import org.springframework.security.access.AccessDecisionManager;
 5 import org.springframework.security.access.AccessDeniedException;
 6 import org.springframework.security.access.ConfigAttribute;
 7 import org.springframework.security.access.SecurityConfig;
 8 import org.springframework.security.authentication.InsufficientAuthenticationException;
 9 import org.springframework.security.core.Authentication;
10 import org.springframework.security.core.GrantedAuthority;
11 //最终的决策
12 //In this method,need to compare authentication with configAttributes.
13 //1、A object is a URL, a filter finds permisssion configuration by this URL,and pass to here
14 //2、Check authentication has attribute in permission configuration(configAttributes)
15 //3、If not match corresponding authentication,throw a AccessDeniedException
16 //这个类中最重要的是decide方法,如果不存在对该资源的定义,直接放行,否则,如果找到正确的角色,即认为拥有权限,否则抛出异常,这样就会进入403页面
17 public class MyAccessDecisionManager implements AccessDecisionManager {
18 
19     public void decide(Authentication authentication, Object object,
20             Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
21             InsufficientAuthenticationException {
22         if(configAttributes==null)
23         {
24             return;
25         }
26         System.out.println(object.toString());//object is a URL
27         Iterator<ConfigAttribute> iterator=configAttributes.iterator();
28         while(iterator.hasNext())
29         {
30             ConfigAttribute ca=iterator.next();
31             String needRoleString=((SecurityConfig)ca).getAttribute();
32             for(GrantedAuthority ga:authentication.getAuthorities())
33             {
34                 if(needRoleString.equals(ga.getAuthority()))
35                 {
36                     //ga is user's role
37                     return;
38                 }
39             }
40         }
41         throw new AccessDeniedException("you have no right");
42 
43     }
44 
45     public boolean supports(ConfigAttribute attribute) {
46         // TODO Auto-generated method stub
47         return true;
48     }
49 
50     public boolean supports(Class<?> clazz) {
51         // TODO Auto-generated method stub
52         return true;
53     }
54 
55 }

这个类中最重要的是decide方法,如果不存在对该资源的定义,直接放行;否则,如果找到正确的角色,即认为拥有权限,并放行,否则

  throw new AccessDeniedException("you have no right");即会进入403.jsp页面。
原文地址:https://www.cnblogs.com/wenhulu/p/5530067.html