Spring Security 入门(1-6-1)Spring Security - 配置文件解析和访问请求处理
1.在pom.xml中添加maven坐标
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.3.RELEASE</version>
</dependency>
2.在web.xml中添加如下配置
<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>
注意filter-name一定要写成springSecurityFilterChain。在DelegatingFilterProxy类init时,如果没有配置targetBeanName,默认会通过filter-name去spring中获取代理的bean。
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
而spring-security的配置是由HttpSecurityBeanDefinitionParser解析器解析,
每一个http都会被解析成一个SecurityFilterChain都添加到FilterChainProxy中的filterChains中。而且该FilterChainProxy会以springSecurityFilterChain注册到spring的bean中。
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition("org.springframework.security.filterChains");
List filterChains = (List)listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
filterChains.add(this.createFilterChain(element, pc));
pc.popAndRegisterContainingComponent();
return null;
}
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
if(!pc.getRegistry().containsBeanDefinition("org.springframework.security.filterChainProxy")) {
RootBeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, "org.springframework.security.filterChains"));
BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
fcpBldr.getRawBeanDefinition().setSource(source);
fcpBldr.addConstructorArgReference("org.springframework.security.filterChains");
fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
AbstractBeanDefinition fcpBean = fcpBldr.getBeanDefinition();
pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, "org.springframework.security.filterChainProxy"));
pc.getRegistry().registerAlias("org.springframework.security.filterChainProxy", "springSecurityFilterChain");
}
}
3.spring-security配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd ">
<!-- 静态资源,不用权限 -->
<http pattern="/resources/**" security="none"/>
<http pattern="/verify/**" security="none"/>
<http pattern="/user/login.htm" security="none"/>
<http pattern="/user/register.*" security="none"/>
<http pattern="/favicon.ico" security="none"/>
<http use-expressions="true" auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint">
<intercept-url pattern="/**" access="authenticated"/>
<!--
<form-login login-page="/user/login.htm" login-processing-url="/login.json" username-parameter="userName"
default-target-url="/user/index.htm" always-use-default-target="true"
authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-handler-ref="authenticationFailureHandler"/>
-->
<logout invalidate-session="true" logout-url="/logout" logout-success-url="/"/>
<csrf disabled="true"/>
<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER"/>
</http>
<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg name="loginFormUrl" value="/user/login.htm" />
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="daoAuthenticationProvider" />
</authentication-manager>
<beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<!-- 是否顯示用戶名不存在信息 -->
<beans:property name="hideUserNotFoundExceptions" value="false"/>
<beans:property name="userDetailsService" ref="userDetailsService"/>
<beans:property name="passwordEncoder" ref="md5Encoder"/>
<beans:property name="saltSource" ref="saltSource"/>
</beans:bean>
<beans:bean id="md5Encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
<!-- 配置自定义过滤器 -->
<beans:bean id="loginFilter" class="com.test.security.LoginFilter">
<beans:property name="filterProcessesUrl" value="/login.json"/>
<beans:property name="usernameParameter" value="userName"/>
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
<beans:property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
</beans:bean>
</beans:beans>
4、启动时候的配置文件解析-每个http解析为一个filterChain。
程序在启动的时候会遍历解析spring-security配置文件,当命名空间是<http>的时候就使用HttpSecurityBeanDefinitionParser类来解析。
private BeanReference createFilterChain(Element element, ParserContext pc) {
boolean secured = !"none".equals(element.getAttribute("security"));
if(!secured) {
if(!StringUtils.hasText(element.getAttribute("pattern")) && !StringUtils.hasText("request-matcher-ref")) {
pc.getReaderContext().error("The \'security\' attribute must be used in combination with the \'pattern\' or \'request-matcher-ref\' attributes.", pc.extractSource(element));
}
for(int var15 = 0; var15 < element.getChildNodes().getLength(); ++var15) {
if(element.getChildNodes().item(var15) instanceof Element) {
pc.getReaderContext().error("If you are using <http> to define an unsecured pattern, it cannot contain child elements.", pc.extractSource(element));
}
}
return this.createSecurityFilterChainBean(element, pc, Collections.emptyList());
}
else
{
BeanReference portMapper = this.createPortMapper(element, pc);
RuntimeBeanReference portResolver = this.createPortResolver(portMapper, pc);
ManagedList authenticationProviders = new ManagedList();
BeanReference authenticationManager = this.createAuthenticationManager(element, pc, authenticationProviders);
boolean forceAutoConfig = isDefaultHttpConfig(element);
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
authenticationProviders.addAll(authBldr.getProviders());
ArrayList unorderedFilterChain = new ArrayList();
unorderedFilterChain.addAll(httpBldr.getFilters());
unorderedFilterChain.addAll(authBldr.getFilters());
unorderedFilterChain.addAll(this.buildCustomFilterList(element, pc));
Collections.sort(unorderedFilterChain, new OrderComparator());
this.checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));
ManagedList filterChain = new ManagedList();
Iterator var13 = unorderedFilterChain.iterator();
while(var13.hasNext()) {
OrderDecorator od = (OrderDecorator)var13.next();
filterChain.add(od.bean);
}
return this.createSecurityFilterChainBean(element, pc, filterChain);
}
}
当发现security="none"的时候,则创建一个DefaultFilterChain添加到FilterChainProxy的filterChains属性中。
当没有security="none"则使用else中的代码。
特别注意HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
最后添加到filterChains中。
5、运行时-根据请求url获得过滤器链--找到第一个匹配的filterChain处理请求?????????
spring-security执行则是依据请求的URL获得过滤器链。然后依次执行。
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall
.getFirewalledResponse((HttpServletResponse) response);
List<Filter> filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0)
{
if (logger.isDebugEnabled())
{
logger.debug(UrlUtils.buildRequestUrl(fwRequest)+ (filters == null ? " has no matching filters": " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
/**
* Returns the first filter chain matching the supplied URL.
*
* @param request the request to match
* @return an ordered array of Filters defining the filter chain
*/
private List<Filter> getFilters(HttpServletRequest request)
{
for (SecurityFilterChain chain : filterChains)
{
if (chain.matches(request))
{
return chain.getFilters();
}
}
return null;
}
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " reached end of additional filter chain; proceeding with original chain");
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
originalChain.doFilter(request, response);
}
else {
currentPosition++;
Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " at position " + currentPosition + " of " + size
+ " in additional filter chain; firing Filter: '"
+ nextFilter.getClass().getSimpleName() + "'");
}
nextFilter.doFilter(request, response, this);
}
}
整个spring-security理解起来其实并不难。但要融入到程序中合理使用,还需要多练习。
Spring Security 入门(1-6-1)Spring Security - 配置文件解析和访问请求处理的更多相关文章
- 【转】Spring Boot干货系列:(二)配置文件解析
转自:Spring Boot干货系列:(二)配置文件解析 前言 上一篇介绍了Spring Boot的入门,知道了Spring Boot使用"习惯优于配置"(项目中存在大量的配置,此 ...
- Spring Boot干货系列:(二)配置文件解析
Spring Boot干货系列:(二)配置文件解析 2017-02-28 嘟嘟MD 嘟爷java超神学堂 前言 上一篇介绍了Spring Boot的入门,知道了Spring Boot使用“习惯优于 ...
- Spring Security 入门(1-7)Spring Security - Session管理
参考链接:https://xueliang.org/article/detail/20170302232815082 session 管理 Spring Security 通过 http 元素下的子元 ...
- Spring Security 入门(1-3-5)Spring Security - remember me!
Remember-Me 功能 概述 Remember-Me 是指网站能够在 Session 之间记住登录用户的身份,具体来说就是我成功认证一次之后在一定的时间内我可以不用再输入用户名和密码进行登录了, ...
- Spring Security 入门(1-6-2)Spring Security - 内置的filter顺序、自定义filter、http元素和对应的filterChain
Spring Security 的底层是通过一系列的 Filter 来管理的,每个 Filter 都有其自身的功能,而且各个 Filter 在功能上还有关联关系,所以它们的顺序也是非常重要的. 1.S ...
- Spring Security 入门(1-3-2)Spring Security - http元素 - intercept-url配置
http元素下可以配置登录页面,也可以配置 url 拦截. 1.直接配置拦截url和对应的访问权限 <security:http use-expressions="false" ...
- Spring Security 入门(1-4-2)Spring Security - 认证过程之AuthenticationProvider的扩展补充说明
1.用户信息从数据库获取 通常我们的用户信息都不会向第一节示例中那样简单的写在配置文件中,而是从其它存储位置获取,比如数据库.根据之前的介绍我们知道用户信息是通过 UserDetailsService ...
- Spring Security 入门(1-3-1)Spring Security - http元素 - 默认登录和登录定制
登录表单配置 - http 元素下的 form-login 元素是用来定义表单登录信息的.当我们什么属性都不指定的时候 Spring Security 会为我们生成一个默认的登录页面. 如果不想使用默 ...
- Spring Security 入门(1-2)Spring Security - 从 配置例子例子 开始我们的学习历程
1.Spring Security 的配置文件 我们需要为 Spring Security 专门建立一个 Spring 的配置文件,该文件就专门用来作为 Spring Security 的配置. &l ...
随机推荐
- JS如何判断滚动条是否滚到底部
判断滚动条到底部,需要用到DOM的三个属性值,即scrollTop.clientHeight.scrollHeight. scrollTop为滚动条在Y轴上的滚动距离. clientHeigh ...
- MySQL的奇怪的删表数据文件而表照样能打开
MySQL的奇怪的删表数据文件而表照样能打开 author:headsen chen 2017-11-02 17:57:17 现象:删除一个正在运行的mysql数据库的表的数据文件:* ...
- PHP实现KMP算法
KMP算法是一种比较高效的字符串匹配算法,关于其讲解,可参考文章 字符串匹配的KMP算法,本文只给出具体的PHP代码实现. /** * @desc构建next数组 * @param string $s ...
- S/4 HANA中的ACDOCT和FAGLFLEXT
最近的几个需求让我对ACDOCT和FAGLFLEXT这两个财务相关表(准确地说是视图)产生了一些了解,同时也发现某些开发同行和业务顾问并没有认识到这些东西.因此打算从技术角度来说明一下这两个视图在S4 ...
- 用three.js创建一个简易的天空盒
本文创建的天空盒是用六张图片来创建的.笔者会论述两种方法来创建,都是最简单基本的方法,不涉及着色器的使用.一种是创建一个盒子,然后将图片作为盒子6个面的纹理贴上来创建.另一种则是简单的将纹理作为场景的 ...
- 笔记:Struts2 国际化
视图页面的国际化 在JSP页面中指定国际化资源需要借助<s:i18n-/>标签,如果把<s:i18n-/>标签作为<s:test-/>标签的父标签,则会直接加载父标 ...
- FFmpeg视频处理
FFmpeg是一个用于音视频处理的自由软件,被广泛用于音视频开发.FFmpeg功能强大,本文主要介绍如何使用FFmpeg命令行工具进行简单的视频处理. 安装FFmpeg可以在官网下载各平台软件包或者静 ...
- 目前微服务/REST的最佳技术栈
服务端 日期 编程语言 Web Framework DbAccess/ORM 数据库 2017-09-07 Node.js 8.4 Koa 2.3 sequelize 4.8 MySQL 2017-0 ...
- 新手立体四子棋AI教程(3)——极值搜索与Alpha-Beta剪枝
上一篇我们讲了评估函数,这一篇我们来讲讲立体四子棋的搜索函数. 一.极值搜索 极值搜索是game playing领域里非常经典的算法,它使用深度优先搜索(因为限制最大层数,所以也可以称为迭代加深搜索) ...
- CAS 之 Https And Database Authentication(三)
CAS 之 Https And Database Authentication(三) 标签(空格分隔): CAS sso-examples-guides源码 Intro(介绍) 由上节可知Apereo ...