Spring Security 3.1

原文地址 http://blog.csdn.net/u012367513/article/details/38866465
spring security 是现在比较流行的安全框架了,可以很容易的集成到项目中实现认证与授权的管理。本文基于spring security 3.1.3版本,主要参考了
L-二当家的的分享
LocalFilterSecurityInterceptor.java 登陆后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法
package security;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

public class LocalFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

    //配置文件注入
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    //登陆后,每次访问资源都通过这个拦截器拦截
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public Class<? extends Object> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        //fi里面有一个被拦截的url
        //里面调用LocalInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
        //再调用LocalAccessDecisionManager的decide方法来校验用户的权限是否足够
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            //执行下一个拦截器
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }

    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
        this.securityMetadataSource = newSource;
    }

    public void destroy() {

    }

    public void init(FilterConfig arg0) throws ServletException {

    }

}
LocalInvocationSecurityMetadataSource 这个用来加载资源与权限的全部对应关系的,并提供一个通过资源获取所有权限的方法
package security;

import java.util.*;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import util.AntUrlPathMatcher;
import util.UrlMatcher;

public class LocalInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    private UrlMatcher urlMatcher = new AntUrlPathMatcher();
    private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

    //tomcat启动时实例化一次  
    public LocalInvocationSecurityMetadataSource() {
        loadResourceDefine();
    }
    //tomcat开启时加载一次,加载所有url和权限(或角色)的对应关系  
    private void loadResourceDefine() {
        resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
        Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
        ConfigAttribute ca = new SecurityConfig("ROLE_USER");
        atts.add(ca);
        resourceMap.put("/index.jsp", atts);
        Collection<ConfigAttribute> attsno =new ArrayList<ConfigAttribute>();
        ConfigAttribute cano = new SecurityConfig("ROLE_NO");
        attsno.add(cano);
        resourceMap.put("/other.jsp", attsno);

        Collection<ConfigAttribute> attsadmin =new ArrayList<ConfigAttribute>();
        ConfigAttribute canoadmin = new SecurityConfig("ROLE_ADMIN");
        attsadmin.add(canoadmin);
        resourceMap.put("/admin.jsp", attsadmin);
    }

    //参数是要访问的url,返回这个url对于的所有权限(或角色)  
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
        // 将参数转为url      
        String url = ((FilterInvocation)object).getRequestUrl();
        Iterator<String>ite = resourceMap.keySet().iterator();
        while (ite.hasNext()) {
            String resURL = ite.next();
            if (urlMatcher.pathMatchesUrl(resURL, url)) {
                configAttributes.addAll(resourceMap.get(resURL));
            }
        }
        return configAttributes.isEmpty() ? null : configAttributes;
    }
    public boolean supports(Class<?>clazz) {
        return true;
    }
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
}
LocalUserDetailService.java 提供通过用户名获取用户所有(包含权限)信息的接口
package security;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.ArrayList;
import java.util.Collection;

public class LocalUserDetailService implements UserDetailsService {

    //登陆验证时,通过username获取用户的所有权限信息,
    //并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException, DataAccessException {
        Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();

        SimpleGrantedAuthority auth1=new SimpleGrantedAuthority("ROLE_ADMIN");
        SimpleGrantedAuthority auth2=new SimpleGrantedAuthority("ROLE_USER");
        SimpleGrantedAuthority auth3=new SimpleGrantedAuthority("ROLE_NO");
        //用户名是admin是天啊及所有权限
        if(username.equals("admin")){
            auths.add(auth1);
            auths.add(auth2);
            auths.add(auth3);
        }else {
            //其他情况去掉admin权限
            auths.add(auth2);
            auths.add(auth3);
        }
        //这里写死密码为admin,正常情况应该从数据库中查询
        User user = new User(username, "admin", true, true, true, true, auths);
        return user;
    }
}
LocalAccessDecisionManager.java 判断用户是否具有访问资源的权限
package security;

import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

public class LocalAccessDecisionManager implements AccessDecisionManager {

    //检查用户是否够权限访问资源  
    //参数authentication是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息  
    //参数object是url  
    //参数configAttributes所需的权限  
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        if (configAttributes == null) {
            return;
        }

        Iterator<ConfigAttribute> ite = configAttributes.iterator();
        while (ite.hasNext()) {
            ConfigAttribute ca = ite.next();
            String needRole = ca.getAttribute();
            for (GrantedAuthority ga : authentication.getAuthorities()) {
                if (needRole.equals(ga.getAuthority())) {
                    return;
                }
            }
        }
        //注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面  
        throw new AccessDeniedException("no right");
    }

    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    public boolean supports(Class<?> clazz) {
        return true;
    }
}
AntUrlPathMatcher.java 匹配url工具类
package util;

import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

public class AntUrlPathMatcher implements UrlMatcher {
    private boolean requiresLowerCaseUrl;
    private PathMatcher pathMatcher;

    public AntUrlPathMatcher()   {
        this(true);

    }

    public AntUrlPathMatcher(boolean requiresLowerCaseUrl)
    {
        this.requiresLowerCaseUrl = true;
        this.pathMatcher = new AntPathMatcher();
        this.requiresLowerCaseUrl = requiresLowerCaseUrl;
    }

    public Object compile(String path) {
        if (this.requiresLowerCaseUrl) {
            return path.toLowerCase();
        }
        return path;
    }

    public void setRequiresLowerCaseUrl(boolean requiresLowerCaseUrl){

        this.requiresLowerCaseUrl = requiresLowerCaseUrl;
    }

    public boolean pathMatchesUrl(Object path, String url) {
        if (("/**".equals(path)) || ("**".equals(path))) {
            return true;
        }

        return this.pathMatcher.match((String)path, url);
    }

    public String getUniversalMatchPattern() {
        return"/**";
    }

    public boolean requiresLowerCaseUrl() {
        return this.requiresLowerCaseUrl;
    }

    public String toString() {
        return super.getClass().getName() + "[requiresLowerCase='"
                + this.requiresLowerCaseUrl + "']";
    }
}
UrlMatcher.java 工具接口
package util;

public interface UrlMatcher{
    Object compile(String paramString);

    boolean pathMatchesUrl(Object paramObject, String paramString);

    String getUniversalMatchPattern();

    boolean requiresLowerCaseUrl();
}
securityConfig.xml spring-security 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
         xmlns:b="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <!--登录页面不过滤 -->
    <http pattern="/login.jsp" security="none" />
    <http access-denied-page="/accessDenied.jsp">
        <form-login login-page="/login.jsp" />
        <!--访问/admin.jsp资源的用户必须具有ROLE_ADMIN的权限 -->
        <!-- <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /> -->
        <!--访问/**资源的用户必须具有ROLE_USER的权限 -->
         <intercept-url pattern="/**" access="ROLE_USER" />
        <session-management>
            <concurrency-control max-sessions="1"
                                 error-if-maximum-exceeded="false" />
        </session-management>
        <!--增加一个filter,这点与 Acegi是不一样的,不能修改默认的filter了, 这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->
        <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR" />
    </http>
    <!--一个自定义的filter,必须包含 authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
        我们的所有控制将在这三个类中实现,解释详见具体配置 -->
    <b:bean id="myFilter"
            class="security.LocalFilterSecurityInterceptor">
        <b:property name="authenticationManager" ref="authenticationManager" />
        <b:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
        <b:property name="securityMetadataSource" ref="securityMetadataSource" />
    </b:bean>
    <!--验证配置,认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="myUserDetailService">
            <!--如果用户的密码采用加密的话 <password-encoder hash="md5" /> -->
        </authentication-provider>
    </authentication-manager>
    <!--在这个类中,你就可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等 -->
    <b:bean id="myUserDetailService" class="security.LocalUserDetailService" />
    <!--访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
    <b:bean id="myAccessDecisionManagerBean"
            class="security.LocalAccessDecisionManager">
    </b:bean>
    <!--资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->
    <b:bean id="securityMetadataSource"
            class="security.LocalInvocationSecurityMetadataSource" />

</b:beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
  <!--加载Spring XML配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value> classpath:securityConfig.xml</param-value>
  </context-param>
  <!-- Spring Secutiry3.1的过滤器链配置 -->
  <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>
  <!-- Spring 容器启动监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--系统欢迎页面 -->
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
accessDenied.jsp 权限不足页面
<%@page pageEncoding="utf-8"%>
<!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>My JSP 'accessDenied.jsp' starting page</title>
</head>
<body>
accessDenied!!!
</body>
</html>
admin.jsp 管理员页面
<%@page pageEncoding="utf-8"%>
<!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>My JSP 'admin.jsp' starting page</title>
</head>
<body>
欢迎来到管理员页面.
</body>
</html>
index.jsp 主页
<%@page pageEncoding="UTF-8"%>
<!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>My JSP 'index.jsp' starting page</title>
</head>

<body>
<h3>这是首页</h3>欢迎
<a href="admin.jsp">进入admin页面</a>
<a href="other.jsp">进入其它页面</a>
</body>
</html>
login.jsp
<%@page pageEncoding="UTF-8"%>
<!DOCTYPEhtmlPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>登录</title>
</head>
<body>
<form action ="j_spring_security_check" method="POST">
    <table>
        <tr>
            <td>用户:</td>
            <td><input type ='text' name='j_username'></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type ='password' name='j_password'></td>
        </tr>
        <tr>
            <td><input name ="reset" type="reset"></td>
            <td><input name ="submit" type="submit"></td>
        </tr>
    </table>
</form>
</body>
</html>
other.jsp
<%@ page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>My JSP 'other.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
</head>
<body>
<h3>这里是Other页面</h3>
</body>
</html>
pom 依赖
<properties>
    <spring-security.version>3.1.3.RELEASE</spring-security.version>
    <spring.version>4.3.10.RELEASE</spring.version>
    <servlet-api.version>3.0-alpha-1</servlet-api.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <version>${spring-security.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-taglibs</artifactId>
      <version>${spring-security.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <version>${spring-security.version}</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>${servlet-api.version}</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值