Spring Security之配置体系

前言

本文跟大家聊聊Spring Security,看似简单易用的配置,实际上很难理解的配置体系。

配置体系概要

Spring Security的配置体系在我看来,主要分为两大类:

  • 负责构建核心组件的SecurityBuilder<O>
  • 负责对核心组件的Builder进行配置的SecurityConfigurer<O, B>

核心组件

先大致介绍一下Spring Security的核心组件,如此我们才能更好的理解相关配置。

  • FilterChainProxy
    这是个Filter。实际上是SpringSecurity的全权代理。从名字我们也能看出,他是一个FilterChain的“代理”。这里的“代理”类似于明星的经纪人,就是负责接活的人。而FilterChain是干活的。FilterChain代表的是一个基于SpringSecurity的安全过滤器链条。很明显,他是一系列Filter的组合。

    在请求到来时,FilterChainProxy先遍历所有的SecurityFilterChain,然后找到匹配的SecurityFilterChain,遍历其中的Filter并调用doFilter方法,从而实现安全过滤器的逻辑。

  • DefaultSecurityFilterChain
    这是FilterChainProxy的助手,同时也是一个完整的安全过滤器链条。他为FilterChainProxy提供了满足某一路径条件所有Filter。

这两级结构,为下面这一场景提供了可靠的安全解决方案:
应用中存在多个DispatcherServlet,分别负责不同的请求路径。然后二者负责的功能,所要求的安全性不同。例如:一个为手机端应用提供服务,面向广大市民,认证机制使用的是JWT。另一个负责的是后台管理服务,认证机制使用的是Session,面向企业管理人员。
按照两级结构,就基于两个DispatcherServlet的路径配置不同的SecurityFilterChain,交给FilterChainProxy即可。

核心组件的构造器

Spring Security抽象出一个共同的接口,两个不同的构造器来分配构造上面两个核心组件。

  • 构造器接口:
public interface SecurityBuilder<O> {
	O build() throws Exception;
}

这个也就是开头提到构造体系的核心组件之一。

  • 两个核心组件的建造者
    1. FilterChainProxy的构造器:WebSecurity
      这个相对简单一些,不准备细聊,感兴趣的可以自己看看源码。
    2. DefaultSecurityFilterChain的构造器:HttpSecurity
      这个是我们主要配置的重点,也是复杂度爆表的。我们将重点聊这个。

HttpSecurity

为了搞清楚,我们需要先从HttpSecurity的“族谱及社会关系”开始。
HttpSecurity的“族谱及社会关系”
UML中的O,是DefaultSecurityFilterChain。
B和H都指代的则是构建者,也就是HttpSecurity 。B的作用之一就是方便你链式调用。

PS: 为了不至于上来就劝退,决定由浅入深的啃图里的类/接口。

AbstractSecurityBuilder

/**
 * 基本的SecurityBuilder,主要是确保对象只构建一次。
 */
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
    @Override
 	public final O build() throws Exception {
 		// 利用AtomicBoolean,确保只构建一次
 		if (this.building.compareAndSet(false, true)) {
 			this.object = doBuild();
 			return this.object;
 		}
 		throw new AlreadyBuiltException("This object has already been built");
 	}
 	// 真正的构建方法,由继承者实现。
 	protected abstract O doBuild() throws Exception;
 }

HttpSecurityBuilder

Http请求的安全构建者。这是个接口,定义的是Http请求的安全构建者的行为。虽然目前只有一个实现类HttpSecurity。但是大家一定要注意,从设计上的职责和定义上进行区分。

public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
	extends SecurityBuilder<DefaultSecurityFilterChain> {
	// 管理configurerer:2个方法
	<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz);
	<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz);
	// 管理sharedObjects:2个方法
	<C> void setSharedObject(Class<C> sharedType, C object);
	<C> C getSharedObject(Class<C> sharedType);
	// 这两个方法与登录有关,也是web安全的一部分。
	H authenticationProvider(AuthenticationProvider authenticationProvider);
	H userDetailsService(UserDetailsService userDetailsService) throws Exception;
	// 添加过滤器相关
	H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);
	H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);
	H addFilter(Filter filter);
}

在职责上,继承了SecurityBuilder,是DefaultSecurityFilterChain的构建者。
在定义上,引入了另一个泛型:H extends HttpSecurityBuilder<H>。第一眼是不是感觉云里雾里,不明就里?其实,我个人认为,这个HttpSecurityBuilder名字并不是很恰当的。我觉得ConfigurableSecurityBuilder或许更恰当。因为他定义的行为包括三部分:管理SecurityConfigurer、管理共享对象、过滤器相关。而以上行为的本质还是告诉建造者,如何构建DefaultSecurityFilterChain,可以理解为建造者的配置。
而H的作用是,限定行为/接口方法中的SecurityConfigurer的。而SecurityConfigurer本身又带2个泛型,等会儿我们再细聊。

SecurityConfigurer

主要用于配置SecurityBuilder,帮助SecurityBuilder构建O。

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
	void init(B builder) throws Exception;
	void configure(B builder) throws Exception;
}

O是建造者B要建造的目标对象。这个类定义的意思是,我可以配置负责构造O的Builder。

init方法,可以初始化自身,也可以初始化builder(例如,提前设置一些属性)。

configurer方法,就是配置builder的重头戏了。一般来说,都是往builder里添加过滤器。

SecurityConfigurer有一个抽象类指定配置生产DefaultSecurityFilterChain的HttpSecurityBuilder。

public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
		extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {}

对应到SecurityConfigurer的泛型:
O为DefaultSecurityFilterChain, H为B。

B限定于生产DefaultSecurityFilterChain的HttpSecurityBuilder。额,还是觉得挺乱的是吧?从这里开始,复杂度就开始飙升了。系好安全带,开车!

先用一句话概括:AbstractHttpConfigurer的职责是配置生产DefaultSecurityFilterChain的HttpSecurityBuilder。按照这个定义,我们简化一下类定义,删除掉泛型H,改成下面:

public abstract class AbstractHttpConfigurer<B extends HttpSecurityBuilder>
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B>

虽然定义还是很长,但已经能理解了。此时只有一个泛型B了,而泛型B的上界是HttpSecurityBuilder。
那么原来的T到底是要干嘛呢?从定义看:T extends AbstractHttpConfigurer<T, B>;无非就是指待某个具体的AbstractHttpConfigurer呗。因此,个人认为,只有一个作用,为了方便方法调用:方便返回值或入参的类型自动转换。

AbstractConfiguredSecurityBuilder

通过SecurityConfigurer进行配置的SecurityBuilder的抽象类。

 public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
   extends AbstractSecurityBuilder<O> {
   // 记录每一个SecurityConfigurer的Class对应的所有SecurityConfigurer注册实例。
   private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
   // 共享对象。这里涉及到需要多个过滤器协作的对象。
   private final Map<Class<?>, Object> sharedObjects = new HashMap<>();
   // 为了支持对象的初始化。
   private ObjectPostProcessor<Object> objectPostProcessor;
   private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
   	// 为了节省篇幅,简单说一下功能:登记/注册configurer到this.configurers
   }

   /**
   * 核心构建方法:模板模式
    */
   @Override
   protected final O doBuild() throws Exception {
       // 同步锁
   	synchronized (this.configurers) {
   		// 初始化阶段
   		this.buildState = BuildState.INITIALIZING;
   		// 初始化前:允许子类进行初始化前的预处理。-钩子方法
   		beforeInit();
   		// 初始化:调用所有的configurers的init方法
   		init();
   		// 配置阶段
   		this.buildState = BuildState.CONFIGURING;
   		// 配置前:允许子类进行配置前的预处理。-钩子方法
   		beforeConfigure();
   		// 进行配置:调用所有的configurers的configure方法
   		configure();
   		// 构建阶段
   		this.buildState = BuildState.BUILDING;
   		// 执行构建;与其他方法不同,这是个抽象方法。因为只有具体实现类才知道要new什么对象。
   		O result = performBuild();
   		// 构建完成
   		this.buildState = BuildState.BUILT;
   		return result;
   	}
   }
}

不知道你们有没有发现,这个类的定义与前面的SecurityConfigurer在泛型上,是一模一样的!这样的安排都是为了使用SecurityConfigurer。

  • 小结一下:
    作用一:支持通过SecurityConfigurer对构建者进行配置。
    作用二:支持对构建对象的IOC初始化过程,例如:afterPropertiesSet()、各种Aware的相关方法的调用。当然也可以通过注册自定义的ObjectPostProcessor,对目标对象进行修改。
    作用三:通过模板模式,统一对象O的构建步骤/流程。基于SecurityConfigurer进行配置的构建流程。

Customizer<T>

没想到吧?还有一个编外人员。别紧张,他是我们用来修改SecurityConfigurer的。上一篇文章的demo,就是一个Customizer。他的原理/思路也十分简单,就是我把当前对象丢给你,你想配置什么,自己设置就是了。例如:

httpSecurity.csrf(csrfConfigurer -> csrfConfigurer.ignoringRequestMatchers(“/**/*.do”))`。

这意思就是,我给你传一个配置HttpSecurity的CsrfConfigurer,HttpSecurity会调用Customizer#customize方法,然后就能执行到你配置的csrfConfigurer.ignoringRequestMatchers("/**/*.do"),对路径/**/*.do不校验csrfToken。
可能有同学不熟悉lambda表达式。上面csrf方法传入的参数等价于

// CsrfConfigurer<HttpSecurity>是配置HttpSecurity的Configurer,他会引入CsrfFilter。
new Customizer<CsrfConfigurer<HttpSecurity>>() {
	public void customize(CsrfConfigurer csrfConfigurer) {
		csrfConfigurer.ignoringRequestMatchers("/**/*.do");
	}
}

HttpSecurity的详解

好了,终于到这位大爷了。

public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
		implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {
	private final RequestMatcherConfigurer requestMatcherConfigurer;
	private List<OrderedFilter> filters = new ArrayList<>();
	// 
	private FilterOrderRegistration filterOrders = new FilterOrderRegistration();
	private AuthenticationManager authenticationManager;
	
	public HttpSecurity csrf(Customizer<CsrfConfigurer<HttpSecurity>> csrfCustomizer) throws Exception {
		ApplicationContext context = getContext();
		// 这里调用了3个方法,依次为:
		// 1. 创建CsrfConfigurer
		// 2. 类似于CAS,如果存在,就直接返回,否则就注册。
		// 3. 调用Customizer.customize方法配置CsrfConfigurer
		csrfCustomizer.customize(getOrApply(new CsrfConfigurer<>(context)));
		return HttpSecurity.this;
	}
	private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer)
			throws Exception {
		// 从已注册的configurers中获取对应的configurer
		// 之所以会这样设计,是因为需要对HttpSecurity进行一些默认配置。后面会说。
		// 因此需要确保配置的是同一个对象。
		C existingConfig = (C) getConfigurer(configurer.getClass());
		if (existingConfig != null) {
			return existingConfig;
		}
		return apply(configurer);
	}
	public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
		// 设置configurer的objectPostProcessor。后面configurer在配置时,会通过他对某些对象进行初始化。如此才能完成ObjectPostProcessor的初始化工作。
		configurer.addObjectPostProcessor(this.objectPostProcessor);
		// 构建者,为了便于configurer获取共享对象。
		configurer.setBuilder((B) this);
		// 调用父类的add方法注册configurer
		add(configurer);
		return configurer;
	}
	@Override
	protected DefaultSecurityFilterChain performBuild() {
		// 对过滤器进行排序
		this.filters.sort(OrderComparator.INSTANCE);
		List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
		for (Filter filter : this.filters) {
			sortedFilters.add(((OrderedFilter) filter).filter);
		}
		// 构建DefaultSecurityFilterChain
		return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
	}
}

由于可以配置的SecurityConfigurer实在是太多了,这里挑了相对简单的CsrfConfigurer作为典型案例。具体的过程,如摘抄的源码上的注释,就不多啰嗦了。

通过SecurityConfigurer引入Filter

不知道大家有没有这样一个疑问:我们并没有看到CsrfFilter是如何添加到HttpSecurity的filters属性,只看到引入的CsrfConfigurer。而这就是,我们SecurityConfigurer最为重要的存在意义:引入Filter。
AbstractConfiguredSecurityBuilder#doBuild方法,我们知道在真正构建目标对象之前,会先后调用SecurityConfigurer#initSecurityConfigurer#configure方法。而引入Filter的密码就藏在configure方法中。

public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
		
	@Override
	public void configure(H http) {
		// 创建CsrfFilter
		CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
		RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
		if (requireCsrfProtectionMatcher != null) {
			// 指定需要防御CSRF的请求
			filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
		}
		AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
		if (accessDeniedHandler != null) {
			// 指定访问异常处理器
			filter.setAccessDeniedHandler(accessDeniedHandler);
		}
		LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
		if (logoutConfigurer != null) {
			// 登出接口也进行CSRF防御
			logoutConfigurer.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
		}
		SessionManagementConfigurer<H> sessionConfigurer = http.getConfigurer(SessionManagementConfigurer.class);
		if (sessionConfigurer != null) {
			// 这里涉及Session管理,后面聊认证的时候再细聊
			sessionConfigurer.addSessionAuthenticationStrategy(getSessionAuthenticationStrategy());
		}
		// 通过ObjectPostProcessor完成初始化,或者修改
		filter = postProcess(filter);
		// 向HttpSecurity中添加CsrfFilter
		http.addFilter(filter);
	}
}

如源码所示,其configure方法的核心业务流程为;

  1. 创建Filter
  2. 根据相关配置设置Filter
  3. 通过ObjectPostProcessor完成初始化
  4. 向HttpSecurity中添加Filter

几乎所有与Filter相关的SecurityConfigurer都是这样对HttpSecurity实现配置的。而配置的重点也就是向HttpSecurity中添加Filter。

HttpSecurity默认配置

新版本的默认配置在HttpSecurityConfiguration

class HttpSecurityConfiguration {
	@Bean(HTTPSECURITY_BEAN_NAME)
	@Scope("prototype")
	HttpSecurity httpSecurity() throws Exception {
		LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
		// 认证管理器的建造者,会放到
		AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
				this.objectPostProcessor, passwordEncoder);
		authenticationBuilder.parentAuthenticationManager(authenticationManager());
		authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
		// 创建HttpSecurity,同时会将authenticationBuilder放入sharedObjects中
		HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
		// 异步请求管理器,需要支持获取当前请求的用户凭证相关信息
		WebAsyncManagerIntegrationFilter webAsyncManagerIntegrationFilter = new WebAsyncManagerIntegrationFilter();
		webAsyncManagerIntegrationFilter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
		// @formatter:off
		http
			// 引入CsrfFilter
			.csrf(withDefaults())
			.addFilter(webAsyncManagerIntegrationFilter)
			// 引入异常处理器,以便处理权限异常
			.exceptionHandling(withDefaults())
			// 引入HeaderWriterFilter,便于设置默认的安全请求头
			.headers(withDefaults())
			// 引入SessionManagementFilter,以便确保session相关功能
			.sessionManagement(withDefaults())
			// 引入SecurityContextHolderFilter,以便获取当前用户
			.securityContext(withDefaults())
			// 引入RequestCacheAwareFilter,以便恢复认证前的访问异常请求
			.requestCache(withDefaults())
			// 引入AnonymousAuthenticationFilter,管理匿名用户
			.anonymous(withDefaults())
			// 引入SecurityContextHolderAwareRequestFilter,以便基于servletApi实现认证
			.servletApi(withDefaults())
			// 引入DefaultLoginPageGeneratingFilter,以便自动生成登录页面
			.apply(new DefaultLoginPageConfigurer<>());
		// 引入LogoutFilter,以便处理默认的登出请求
		http.logout(withDefaults());
		// @formatter:on
		// 引入基于spring的spi机制引入的configurer,如果开发者没有配置的,目前应该是没有相关的configurer。
		applyDefaultConfigurers(http);
		return http;
	}
}

总结

  1. 配置体系主要包括:SecurityBuilder<O>SecurityConfigurer<O, B>

  2. FilterChainProxy由WebSecurity构建。

  3. DefaultSecurityFilterChain由HttpSecurity构建。

  4. SecurityConfigurer<O, B>负责配置SecurityBuilder,指引SecurityBuilder如何构建。

  5. HttpSecurity本质上是通过一系列的SecurityConfigurer<O, B>创建Filter,来构建DefaultSecurityFilterChain。

  6. 我们配置HttpSecurity的原理如下:

    HttpSecurity -> SecurityConfigurer<O, B> -> Customizer

    本质上是通过Customizer修改SecurityConfigurer,从而改变生成的Filter的属性。

  7. 推荐使用基于Customizer的HttpSecurity方法进行配置。作用清晰:引入某一过滤器,配置明确:写在Customizer里面了。

后记

本文我们讨论了Spring Security的配置体系,希望大家看源码时不至于在懵逼,琢磨某个Filter时不至于迷失在源码里。下一次,我们聊聊UsernamePasswordAuthenticationFilter。相信大家期盼已久了吧。

参照

Spring Security的架构

  • 24
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Web Flow 2.0 入门 本教程分析了 Spring Web Flow 2.0 的技术要点,并且通过创建一个示例应用程序,展示了 Spring Web Flow 2.0 的基本知识。 开始之前 关于本教程 本教程通过一个简化的购物车应用,介绍了如何使用 Spring Web Flow 2.0 来构建 Web 应用程序。本教 程以讲解实例为主,为了读者更好地理解 Spring Web Flow ,也有部分理论的解释。 先决条件 本教程要求读者具备 Java Web 应用的基本知识、熟悉 Spring Framework 的应用。 系统要求 运行本教程中的示例,需要下列工具: • JDK 1.6.0+ • Spring Framework 2.5.4+ 及其依赖项 • Spring Web Flow 2.0.2 • Tomcat 6.0.0+ (为支持 EL , Tomcat 须 6.0 及以上版本) • eclipse 3.2.2+ Spring Web Flow 2.0 概述 Spring Web Flow 2.0 新特性 Spring Web Flow 是 Spring 的一个子项目,其最主要的目的是解决跨越多个请求的、用户与服务器之间 的、有状态交互问题。最新版本为 2.0 ,相比于 1.x 版的 Spring Web Flow ,有以下几个值得注意的新 特性。 • 与 Spring MVC 深度整合 Spring Web Flow 1.x 是个自成体系的框架,可与 Spring Web MVC 、 Struts 、 JSF 等 Web 框架整合。最新的 Spring Web Flow 2.0 则明确声明是基于 Spring Web MVC 的一个扩展。 • 提供了处理 Ajax 事件的能力 Ajax 事件的处理与 Web Flow 事件的处理相一致,在处理完成后, flow 即可刷新客户端相关 界面代码。 • 与 JSF 整合 通过将 JSF 层层包装,最终可在 Spring Framework 和 Spring Web Flow 中使用 JSF 的各种 组件。 • 与 Spring Security (原 Acegi Security )整合 只需将某个 flow 声明为“ secured ”,即可利用 Spring Security 来确定当前用户是否有权限运 行 flow 、激发事件等等。 • 更简洁的配置 官方的数据说同一个 flow , 2.0 版的配置比 1.x 版的配置少 50% 的 XML 代码。 2 • 重用更方便 Spring Web Flow 2.0 提供了 flow 的继承,重用即有的 flow 代码更加容易。 • 重用更方便 Spring Web Flow 2.0 提供了 flow 的继承,重用即有的 flow 代码更加容易。 购物车用例 要了解 Spring Web Flow 是什么东西,最好的办法莫过于查看示例,图 2 展示了一个简化的购物车的流 程。 图 1 购物车示例 图 2 所示流程用 Spring Web Flow 2.0 的配置文件表示如下: 清单 1 用 Spring Web Flow 语义表达购物车流程 …… <flow> <view-state id="viewCart"> <transition on="submit" to="viewOrder"/> </view-state> <view-state id="viewOrder"> <transition on="confirm" to="viewConfirmed"/> </view-state> <view-state id="viewConfirmed"> <transition on="returnToIndex" to="returnToIndex"/> </view-state> <end-state id="returnToIndex"/> </flow> 清单 1 省略了许多技术细节,展示的只是一个业务的流程,主要是为了让大家对 Spring Web Flow 的语 义有个初始的印象。从清单 1 中,应注意到一个很重要的特征—— Spring Web Flow 语义与 Servlet API 3 无关。更确切地讲, Spring Web Flow 语义关注的是业务的流程,并未与 Sun 公司的 Web 规范紧密结 合,这种描述是更高层次的抽象,差不多是在建模的角度来描述业务流程。 不过, Spring Web Flow 也并非只有抽象,现在还没有哪一种工具或语言可以将一个模型直接转换成相应 的应用程序。 Spring Web Flow 更像是抽象建模和技术细节的混血儿,相比于湮没在繁多的控制器和视图 中的 Web MVC 应用来讲, Spring Web Flow 提供了如清单 1 所描述的更高层次的抽象,但同时它也 整合了像 Unified EL 这样的工具来控制技术上的细节。 Spring Web Flow 的基本元素 Flow 可看作是客户端与服务器的一次对话( conversation )。 Flow 的完成要由分多个步骤来实现,在 Spring Web Flow 的语义中,步骤指的就是 state 。 Spring Web Flow 提供了五种 state ,分别是 Action State 、 View State 、 Subflow State 、 Decision State 、 End State ,这些 state 可用于定义 flow 执 行过程中的各个步骤。除了 End State 外,其他 state 都可以转换到别的 state ,一般通过在 state 中 定义 transition 来实现到其他 state 的转换,转换的发生一般由事件( event )来触发。 什么情况下可以使用 Spring Web Flow? 前面讲了, Spring Web Flow 提供了描述业务流程的抽象能力,但对一种 Web 开发技术而言,仅有这 些是不够的。同时, Spring Web Flow 是不是能够取代其他 Web MVC 技术?或者在任何情况下都应优 先使用 Spring Web Flow ?要回答这些问题,先来看一下 Spring Web Flow 所着力解决的技术问题。 Web 应用程序的三种范围 Java Servlet 规范为 Web 应用程序中用到的各种对象规定了三种范围( scope ),分别是 request 范 围、 session 范围和 application 范围。 • request 范围中的对象是跟客户的请求绑定在一起的,每次请求结束都会销毁对象,而新的请求 过来时又会重新创建对象。 request 范围适合存放数据量较大的临时数据。 • session 范围中的对象是跟会话( session )绑定在一起的,每次会话结束会销毁这些对象,而 新的会话中又会重新创建。 HTTP 协议本身是无状态的,服务器和客户端要实现会话的管理,只 能借助于一些辅助的手段,如在协议的数据包中加一些隐藏的记号,等等。 session 范围适合存 放本次会话需要保留的数据。 • application 范围的对象是跟应用程序本身绑定在一起,从 Servlet API 的角度来讲,就是存放在 ServletContext 中的对象,它们随着 Servlet 的启动而创建, Servlet 关闭时才会销毁。 application 范围适合存放那些与应用程序全局相关的数据。 现实开发中最令人头痛的莫过于 session 范围, Java Servlet 规范指明可在 web.xml 中按如下方式配置 session 的有效时间为100 分钟: 清单 2 web.xml 中 session 的配置 <session-config> <session-timeout>100</session-timeout> </session-config> 然而,现实中的 session 范围更像是“鸡肋”,把大量数据放入 session 会导致严重的效率问题,
目录 第一部分spring的核心 第1章开始spring之旅 1.1spring是什么 1.2开始spring之旅 1.3理解依赖注入 1.3.1依赖注入 1.3.2di应用 1.3.3企业级应用中的依赖注入 1.4应用aop 1.4.1aop介绍 1.4.2aop使用 1.5小结 第2章基本bean装配 2.1容纳你的bean 2.1.1beanfactory介绍 2.1.2使用应用上下文 2.1.3bean的生命 2.2创建bean 2.2.1声明一个简单的bean 2.2.2通过构造函数注入 2.3注入bean属性 2.3.1注入简单的数值 2.3.2使用其他的bean 2.3.3装配集合 2.3.4装配空值 2.4自动装配 2.4.1四种自动装配类型 2.4.2混合使用自动和手动装配 2.4.3何时采用自动装配 2.5控制bean创建 2.5.1bean范围化 2.5.2利用工厂方法来创建bean 2.5.3初始化和销毁bean 2.6小结 第3章高级bean装配 3.1声明父bean和子bean 3.1.1抽象基bean类型 3.1.2抽象共同属性 3.2方法注入 3.2.1基本的方法替换 3.2.2获取器注入 3.3注入非springbean 3.4注册自定义属性编辑器 3.5使用spring的特殊bean 3.5.1后处理bean 3.5.2bean工厂的后处理 3.5.3配置属性的外在化 3.5.4提取文本消息 3.5.5程序事件的解耦 3.5.6让bean了解容器 3.6脚本化的bean 3.6.1给椰子上lime 3.6.2脚本化bean 3.6.3注入脚本化bean的属性 3.6.4刷新脚本化bean 3.6.5编写内嵌的脚本化bean 3.7小结 第4章通知bean 4.1aop简介 4.1.1定义aop术语 4.1.2spring对aop的支持 4.2创建典型的spring切面 4.2.1创建通知 4.2.2定义切点和通知者 4.2.3使用proxyfactorybean 4.3自动代理 4.3.1为spring切面创建自动代理 4.3.2自动代理@aspectj切面 4.4定义纯粹的pojo切面 4.5注入aspectj切面 4.6小结 第二部分企业spring 第5章使用数据库 5.1spring的数据访问哲学 5.1.1了解spring数据访问的异常体系 5.1.2数据访问的模板化 5.1.3使用dao支持类 5.2配置数据源 5.2.1使用jndi数据源 5.2.2使用数据源连接池 5.2.3基于jdbc驱动的数据源 5.3在spring里使用jdbc 5.3.1处理失控的jdbc代码 5.3.2使用jdbc模板 5.3.3使用spring对jdbc的dao支持类 5.4在spring里集成hibernate 5.4.1选择hibernate的版本 5.4.2使用hibernate模板 5.4.3建立基于hibernate的dao 5.4.4使用hibernate3上下文会话 5.5spring和java持久api 5.5.1使用jpa模板 5.5.2创建一个实体管理器工厂 5.5.3建立使用jpa的dao 5.6spring和ibatis 5.6.1配置ibatis客户模板 5.6.2建立基于ibatis的dao 5.7缓存 5.7.1配置缓存方案 5.7.2缓存的代理bean 5.7.3注解驱动的缓存 5.8小结 第6章事务管理 6.1理解事务 6.1.1仅用4个词解释事务 6.1.2理解spring对事务管理的支持 6.2选择事务管理器 6.2.1jdbc事务 6.2.2hibernate事务 6.2.3jpa事务 6.2.4jdo事务 6.2.5jta事务 6.3在spring中编写事务 6.4声明式事务 6.4.1定义事务参数 6.4.2代理事务 6.4.3在spring2.0里声明事务 6.4.4定义注释驱动事务 6.5小结 第7章保护spring 7.1springsecurity介绍 7.2验证用户身份 7.2.1配置providermanager 7.2.2根据数据库验证身份 7.2.3根据ldap仓库进行身份验证 7.3控制访问 7.3.1访问决策投票 7.3.2决定如何投票 7.3.3处理投票弃权 7.4保护web应用程序 7.4.1代理springsecurity的过滤器 7.4.2处理安全上下文 7.4.3
### 回答1: 微服务鉴权中心是指在微服务架构中,使用一个独立的鉴权中心来统一管理和验证各个微服务的访问权限。而网关是作为微服务鉴权中心的入口,负责对外提供服务,同时也是微服务的访问控制和认证的第一层防线。在网关配置中使用Spring Security OAuth2是一种常见的实现方式。 首先,需要在网关项目中引入Spring Security OAuth2的依赖。可以使用Maven或Gradle将所需的依赖加入到项目的配置文件中。 接下来,需要配置Spring Security OAuth2的相关信息,包括鉴权中心的认证服务器地址、客户端ID和密钥等。这些配置可以在网关项目的配置文件中设置。 然后,需要配置网关的请求过滤器,用于拦截并验证请求。可以实现一个自定义的过滤器,继承自Spring Security的AbstractAuthenticationProcessingFilter类,并覆写其中的相关方法。 在过滤器中,需要使用OAuth2的认证流程来验证请求的合法性。可以使用RestTemplate发送请求到鉴权中心的认证服务器,获取访问令牌和授权信息。 接着,可以通过OAuth2AuthenticationManager来管理认证信息,并创建并设置Spring Security的Authentication对象。可以使用自定义的处理器将认证信息传递给后续的微服务。 最后,需要对网关的接口进行权限的配置。可以使用Spring Security的注解来定义接口的访问权限,如@PreAuthorize和@Secured。 通过以上的配置,网关就可以实现对请求的鉴权和认证了。对于未经授权的请求,网关会拒绝访问,并返回相应的错误信息。对于经过鉴权验证的请求,网关会将请求转发给相应的微服务进行处理。同时,网关还可以对返回的结果进行处理和加工,以满足特定的需求。 总之,配置Spring Security OAuth2可以有效地实现微服务鉴权中心之网关的权限管理和访问控制,提高系统的安全性和可维护性。 ### 回答2: 微服务鉴权中心作为一个独立的服务单元,负责管理和维护整个微服务体系的权限和鉴权机制。网关是微服务系统的入口,负责接收和处理所有外部请求。 在配置Spring Security OAuth2时,我们可以将鉴权中心和网关进行整合,以实现统一的认证和授权机制。在网关配置文件中,我们首先需要引入Spring Security OAuth2的依赖,然后定义一些必要的配置项。 首先,我们需要配置认证服务器的地址,通常是鉴权中心的地址。也就是说,所有的认证和授权请求都会转发到该地址进行处理。配置项如下: ``` security: oauth2: client: accessTokenUri: <鉴权中心地址>/oauth/token userAuthorizationUri: <鉴权中心地址>/oauth/authorize clientId: <客户端ID> clientSecret: <客户端密钥> ``` 其中,`accessTokenUri`表示获取访问令牌的地址,`userAuthorizationUri`表示用户授权的地址,`clientId`和`clientSecret`是鉴权中心针对网关颁发的客户端ID和密钥。 接下来,我们需要配置资源服务器的地址,即微服务的地址。配置项如下: ``` security: oauth2: resource: userInfoUri: <微服务地址>/user ``` 其中,`userInfoUri`表示获取用户信息的地址。 最后,我们需要配置路由规则,指定哪些请求需要进行认证和授权。配置项如下: ``` spring: cloud: gateway: routes: - id: <路由ID> uri: <目标URI> predicates: - Path=/api/** filters: - TokenRelay metadata: authorization-uri: <鉴权中心地址>/oauth/authorize ``` 其中,`id`表示路由的唯一标识,`uri`表示目标URI,`predicates`指定路由的条件,`filters`指定过滤器,`metadata`指定认证和授权的地址。 通过以上配置,我们可以实现网关和鉴权中心的整合,实现微服务系统的统一认证和授权机制。网关会将认证和授权请求转发到鉴权中心进行处理,并根据鉴权中心返回的结果进行相应的操作。 ### 回答3: 在微服务架构中,鉴权是一个非常重要的部分,它能够确保只有经过授权的用户才能访问特定的服务。而网关作为整个系统的入口,负责转发和路由请求,需要配置Spring Security和OAuth2来实现鉴权。 首先,需要在网关服务中添加Spring Security和OAuth2的相关依赖,并在配置文件中开启相关功能。接着,需要创建一个自定义的鉴权过滤器,该过滤器会在请求到达网关之后进行鉴权操作。 在过滤器中,首先需要配置一个OAuth2的资源服务。这个资源服务可以通过配置一个TokenStore来获取和保存令牌信息。然后,可以配置一个JwtAccessTokenConverter来验证和解析令牌。同时,需要配置一个ResourceServerTokenServices,该服务会验证令牌的正确性,是否过期等信息。 接下来,在过滤器中需要配置spring security的认证管理器,该管理器会根据请求头中的令牌信息来进行用户的鉴权操作。可以使用一个自定义的UserDetailsService来获取用户的权限信息,并将权限信息添加到SecurityContext中,以便后续的鉴权操作。 最后,在过滤器中,可以配置一些具体的鉴权规则,比如某些URL需要特定的角色才能访问,可以用.antMatchers().hasRole()的方式进行配置。 通过以上步骤,就可以实现网关的鉴权功能。当用户发起请求时,网关会根据请求头中的令牌信息进行鉴权,只有经过授权的用户才能访问特定的服务。这样可以确保整体系统的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值