16、Spring MVC 之 Web Security

来源:互联网 发布:淘宝店铺被永久查封 编辑:程序博客网 时间:2024/06/14 06:19

Spring Security是一个单独的项目,它可以无缝的和Spring MVC集成。Spring Security提供会特性保护web应用来自恶意的攻击。具体可以参看Spring Security的参考文档的章节,例如“CSRF protection”,“Security Response Headers”和“Spring MVC Integration”.注意:使用Spring Security去保护应用不是针对所有的特性。例如CSRF保护可以通过简单的添加CsrfFilter以及CsrfRequestDataValueProcessor来保护你的应用。可以查看Spring MVC Showcase做为一个例子。

另外一个选择是使用专门针对Web安全的框架,HDIV就是这样的一个框架并且可以和Spring MVC集成。

其实上面就是Spring官网对于Spring关于Web Security的介绍。如果就这样我觉得东西太少了。但是如果叫我详细的来解释一下Spring Security那么东西又太多了。那么我就针对一下Spring Security的里面的概念,以及它的入口还有它的源码来给大家讲一下Spring Security是如何做到Web Security的。

1、Spring Security Overview

下面是Spring Security官方的一个关于它的架构图。

这里写图片描述

下面来介绍一下主要的概念:
AbstractSecurityInterceptor:是一个抽象类,实现安全对象的安全拦截,抽象了基于HTTP资源与方法资源的安全验证。
AuthenticationManager:处理验证请求,相当于用户登录验证。
AccessDecisionManager:访问投票管理器。相当于用户权限验证。
SecurityMetadataSource:需要来实现,用于储存并且能够确定ConfigAttribute(用户配置的属性)可以适用于一个给定的安全对象调用。
RunAsManager:创建一个临时的Authentication(验证)对象仅用来验证当前安全对象调用。
AfterInvocationManager:评论安全对象调用返回的对象,能够修改返回的对象或者throw an AccessDeniedException.
MethodSecurityInterceptor:提供基于方法级别的验证。
AspectJMethodSecurityInterceptor:AspectJ JoinPoint安全拦截,包装JoinPoint到MethodInvocation代理使得它能够兼容security只能够支持MethodInvocation基础架构类。
FilterSecurityInterceptor:通过Filter的形式执行HTTP资源的安全处理。

2、Spring Security Entrance

这次我们主要是讲Spring Security关于HTTP资源的安全处理,根据官方文档,使用Spring Security第一件事就是需要在web.xml文件中添加下面的filter描述:

<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 Security web基础设施中。DelegatingFilterProxy是Spring Framework类用于代理到一个Filter实现,它是被声明在你的application context的Spring bean中。在这种情况下,这个bean会被命名为”springSecurityFilterChain”,它是一个被命名空间创建内部的基础设施bean用于处理web安全。注意:你不能够使用你自定义的bean名称。一旦你添加这个到你的web.xml文件中,你就可以开始修改你的application context文件了。Web安全服务配置使用元素。

3、Spring Security Process

首先展示一下Spring Security处理时序图:

这里写图片描述
其实里面最重要还是beforeInvocation(),finallyInvocation(),afterInvocation().下面我们来具体分析一下这三个方法的代码。大家注意结合Spring Security Overview中的概念对比来看,就会清晰的认识到这个组件的真实意义了。

1)beforeInvocation

主要包括用户的验证与授权。

protected InterceptorStatusToken beforeInvocation(Object object) {    Assert.notNull(object, "Object was null");    final boolean debug = logger.isDebugEnabled();    if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {        throw new IllegalArgumentException(                "Security invocation attempted for object "                        + object.getClass().getName()                        + " but AbstractSecurityInterceptor only configured to support secure objects of type: "                        + getSecureObjectClass());    }    // 1、获取用户资源(SecurityMetadataSource)    Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()            .getAttributes(object);    if (attributes == null || attributes.isEmpty()) {        if (rejectPublicInvocations) {            throw new IllegalArgumentException(                    "Secure object invocation "                            + object                            + " was denied as public invocations are not allowed via this interceptor. "                            + "This indicates a configuration error because the "                            + "rejectPublicInvocations property is set to 'true'");        }        if (debug) {            logger.debug("Public object - authentication not attempted");        }        publishEvent(new PublicInvocationEvent(object));        return null; // no further work post-invocation    }    if (debug) {        logger.debug("Secure object: " + object + "; Attributes: " + attributes);    }    if (SecurityContextHolder.getContext().getAuthentication() == null) {        credentialsNotFound(messages.getMessage(                "AbstractSecurityInterceptor.authenticationNotFound",                "An Authentication object was not found in the SecurityContext"),                object, attributes);    }    // 2、用户登录验证(AuthenticationManager)    Authentication authenticated = authenticateIfRequired();    // Attempt authorization    try {        // 3、用户授权验证(AccessDecisionManager)        this.accessDecisionManager.decide(authenticated, object, attributes);    }    catch (AccessDeniedException accessDeniedException) {        publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,                accessDeniedException));        throw accessDeniedException;    }    if (debug) {        logger.debug("Authorization successful");    }    if (publishAuthorizationSuccess) {        publishEvent(new AuthorizedEvent(object, attributes, authenticated));    }    // 4、试图运行一个不同的用户(RunAsManager)    Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,            attributes);    if (runAs == null) {        if (debug) {            logger.debug("RunAsManager did not change Authentication object");        }        // no further work post-invocation        return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,                attributes, object);    }    else {        if (debug) {            logger.debug("Switching to RunAs Authentication: " + runAs);        }        SecurityContext origCtx = SecurityContextHolder.getContext();        SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());        SecurityContextHolder.getContext().setAuthentication(runAs);        // need to revert to token.Authenticated post-invocation        return new InterceptorStatusToken(origCtx, true, attributes, object);    }}

2)finallyInvocation

设置用户的SecurityContext信息。

protected void finallyInvocation(InterceptorStatusToken token) {    if (token != null && token.isContextHolderRefreshRequired()) {        if (logger.isDebugEnabled()) {            logger.debug("Reverting to original Authentication: " + token.getSecurityContext().getAuthentication());        }        // 设置SecurityContext信息        SecurityContextHolder.setContext(token.getSecurityContext());    }}

3)afterInvocation

授权的后置处理,评论安全对象调用返回的对象,能够修改返回的对象或者throw an AccessDeniedException.

protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {    if (token == null) {        // public object        return returnedObject;    }    finallyInvocation(token); // continue to clean in this method for passivity    if (afterInvocationManager != null) {        // Attempt after invocation handling        try {            // 后置处理(AfterInvocationManager)            returnedObject = afterInvocationManager.decide(token.getSecurityContext().getAuthentication(),                    token.getSecureObject(),                    token.getAttributes(), returnedObject);        }        catch (AccessDeniedException accessDeniedException) {            AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token                    .getAttributes(), token.getSecurityContext().getAuthentication(), accessDeniedException);            publishEvent(event);            throw accessDeniedException;        }    }    return returnedObject;}

通过这三个主要的方法是不是把Spring Security当中的组件都组合起来了。就能够达到用户验证与用户授权了。

官网地址:spring-framework-reference-4.2.6.RELEASE

0 0
原创粉丝点击