本文关注应用的安全方面,涉及校验以及授权方面,以springboot自带的security板块作为讲解的内容

实例

建议用户可直接路由至博主的先前博客spring security整合cas方案。本文则针对相关的源码作下简单的分析,方便笔者以及读者更深入的了解spring的security板块

@EnableWebSecurity

这个注解很精髓,基本上可以作为security的入口,笔者贴一下它的源码

  1. @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
  2. @Target(value = { java.lang.annotation.ElementType.TYPE })
  3. @Documented
  4. @Import({ WebSecurityConfiguration.class,
  5. SpringWebMvcImportSelector.class })
  6. @EnableGlobalAuthentication
  7. @Configuration
  8. public @interface EnableWebSecurity {
  9. /**
  10. * Controls debugging support for Spring Security. Default is false.
  11. * @return if true, enables debug support with Spring Security
  12. */
  13. boolean debug() default false;
  14. }

可以分为三个部分来分析,

SpringWebMvcImportSelector-支持mvc的参数安全校验,替代了@EnableWebMvcSecurity注解

WebSecurityConfiguration-Web的安全配置

@EnableGlobalAuthentication-支持公共的认证校验

SpringWebMvcImportSelector

首先先看下其如何整合mvc的安全校验,其是一个ImportSelector接口,观察下其复写的方法

  1. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  2. boolean webmvcPresent = ClassUtils.isPresent(
  3. "org.springframework.web.servlet.DispatcherServlet",
  4. getClass().getClassLoader());
  5. return webmvcPresent
  6. ? new String[] {
  7. "org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration" }
  8. : new String[] {};
  9. }

由上述代码可知,在classpath环境中存在mvc的关键类DispatcherServlet时便会引入WebMvcSecurityConfiguration类,那么此类又配置了什么东西呢?

里面的代码很简单,但关键是其是WebMvcConfigurer接口的实现类,根据之前的文章提到,该接口主要是用于配置MVC的相关功能,比如参数处理器、返回值处理器、异常处理器等等。

而该类只是扩展了相应的参数处理器,我们可以看下源码

  1. @Override
  2. @SuppressWarnings("deprecation")
  3. public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
  4. // 支持@AuthenticationPrinciple参数注解校验
  5. AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();
  6. authenticationPrincipalResolver.setBeanResolver(beanResolver);
  7. argumentResolvers.add(authenticationPrincipalResolver);
  8. // 废弃
  9. argumentResolvers
  10. .add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());
  11. // csrf token参数
  12. argumentResolvers.add(new CsrfTokenArgumentResolver());
  13. }

针对@AuthenticationPrinciple注解的参数校验,本文不展开了,这里作下归纳

  1. 带有@AuthenticationPrinciple注解的参数其值会从SecurityContext的上下文读取相应的Authentication校验信息
  2. 有一个要求,被该注解修饰的参数须同SecurityContext的上下文存放的Authentication信息为同一接口,否则则会返回null。如果设置了errorOnInvalidType属性为true,则会抛异常
  3. 综上所述,该注解主要是方便将校验通过的Token用于参数赋值,其它的作用也不是很大

@EnableGlobalAuthentication

再来分析下springboot-security的公共认证校验是什么概念,贴下源码

  1. @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
  2. @Target(value = { java.lang.annotation.ElementType.TYPE })
  3. @Documented
  4. @Import(AuthenticationConfiguration.class)
  5. @Configuration
  6. public @interface EnableGlobalAuthentication {
  7. }

OK,直接进入相应的AuthenticationConfiguration类进行具体的分析


1.其引入了ObjectPostProcessorConfiguration配置用于创建AutowireBeanFactoryObjectPostProcessor类,作用应该是通过Spring上下文实例相应的实体类并注册到bean工厂中

  1. @Bean
  2. public ObjectPostProcessor<Object> objectPostProcessor(
  3. AutowireCapableBeanFactory beanFactory) {
  4. return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
  5. }

2.创建基于密码机制的认证管理器Bean,类型为DefaultPasswordEncoderAuthenticationManagerBuilder

  1. @Bean
  2. public AuthenticationManagerBuilder authenticationManagerBuilder(
  3. ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
  4. // 密码加密器
  5. LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
  6. // 认证事件传播器
  7. AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);
  8. // 默认的认证管理器
  9. DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
  10. if (authenticationEventPublisher != null) {
  11. result.authenticationEventPublisher(authenticationEventPublisher);
  12. }
  13. return result;
  14. }

上述的密码加密器支持多种方式的加密,比如bcrypt(默认)/ladp/md5/sha-1等,感兴趣的读者可自行阅读。用户也可多用此Bean作额外的扩展,例如官方建议的如下代码

  1. @Configuration
  2. @EnableGlobalAuthentication
  3. public class MyGlobalAuthenticationConfiguration {
  4. @Autowired
  5. public void configureGlobal(AuthenticationManagerBuilder auth) {
  6. auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
  7. .and().withUser("admin").password("password").roles("ADMIN,USER");
  8. }
  9. }

3.创建基于UserDetails的认证器,用于管理用户的授权信息

  1. @Bean
  2. public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
  3. return new InitializeUserDetailsBeanManagerConfigurer(context);
  4. }

其会创建基于Datasource源的DaoAuthenticationProvider认证校验器,前提是ApplicationContext上下文存在UserDetailsServiceBean对象,否则会不创建。如果用户想基于数据库或者其他数据源的可尝试复写UserDetailsService接口

  1. @Configuration
  2. public class DaoUserDetailsServiceConfig {
  3. /**
  4. * load user info by dao
  5. *
  6. * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider
  7. */
  8. @Configuration
  9. public static class DefaultUserDetailsService implements UserDetailsService {
  10. private static final String DEFAULT_PASS = "defaultPass";
  11. // admin authority
  12. private Collection<? extends GrantedAuthority> adminAuthority;
  13. @Resource
  14. private PasswordEncoder defaultPasswordEncoder;
  15. public DefaultUserDetailsService() {
  16. SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_ADMIN");
  17. List<GrantedAuthority> authorities = new ArrayList<>();
  18. authorities.add(authority);
  19. adminAuthority = Collections.unmodifiableList(authorities);
  20. }
  21. @Override
  22. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  23. User userdetails = new User(username, defaultPasswordEncoder.encode(DEFAULT_PASS), adminAuthority);
  24. return userdetails;
  25. }
  26. @Bean
  27. public PasswordEncoder daoPasswordEncoder() {
  28. PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
  29. return passwordEncoder;
  30. }
  31. }
  32. }

注意:实现UserDetailsService的自定义实例请确保只有一个注册至ApplicationContext上,否则上述的基于数据源配置无法自动化配置;但也可通过AuthenticationManagerBuilder#userDetailsService()方法来进行相应的配置


4.创建AuthenticationProvider认证器,用于用户信息的校验

  1. @Bean
  2. public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
  3. return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
  4. }

同第三点,只是它就配置简单的AuthenticationProvider至相应的AuthenticationManagerBuilderBean中

所以综上所述,@EnableGlobalAuthentication注解的主要目的是配置认证管理器,里面包含了加密器以及相应的认证器

WebSecurityConfiguration

web方面的安全配置,笔者也根据加载的顺序来进行分析


1.获取WebSecurityConfigurer接口bean集合的AutowiredWebSecurityConfigurersIgnoreParents

  1. @Bean
  2. public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
  3. ConfigurableListableBeanFactory beanFactory) {
  4. return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
  5. }

此Bean用于获取所有注册在bean工厂上的WebSecurityConfigurer接口,用户也一般通过此接口的抽象类WebSecurityConfigurerAdapter来进行相应的扩展


2.设置Security的Filter过滤链配置,提前为创建过滤链作准备

  1. @Autowired(required = false)
  2. public void setFilterChainProxySecurityConfigurer(
  3. ObjectPostProcessor<Object> objectPostProcessor,
  4. @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
  5. throws Exception {
  6. // WebSecurity创建
  7. webSecurity = objectPostProcessor
  8. .postProcess(new WebSecurity(objectPostProcessor));
  9. if (debugEnabled != null) {
  10. webSecurity.debug(debugEnabled);
  11. }
  12. // 根据@Order属性排序
  13. Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
  14. Integer previousOrder = null;
  15. Object previousConfig = null;
  16. // 校验Order对应的值,不允许相同,否则会抛出异常
  17. for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
  18. Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
  19. if (previousOrder != null && previousOrder.equals(order)) {
  20. throw new IllegalStateException(
  21. "@Order on WebSecurityConfigurers must be unique. Order of "
  22. + order + " was already used on " + previousConfig + ", so it cannot be used on "
  23. + config + " too.");
  24. }
  25. previousOrder = order;
  26. previousConfig = config;
  27. }
  28. // 对排序过的SecurityConfigurer依次放入WebSecurity对象中
  29. for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
  30. webSecurity.apply(webSecurityConfigurer);
  31. }
  32. this.webSecurityConfigurers = webSecurityConfigurers;
  33. }

这里便提一下,我们在继承WebSecurityConfigurerAdapter抽象类的时候,记得在其头上加上@Order属性,并且保证值唯一


3.创建Security过滤链

  1. @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
  2. public Filter springSecurityFilterChain() throws Exception {
  3. // 如果用户没有配置WebSecurityConfigurer接口,则创建一个空的
  4. boolean hasConfigurers = webSecurityConfigurers != null
  5. && !webSecurityConfigurers.isEmpty();
  6. if (!hasConfigurers) {
  7. WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
  8. .postProcess(new WebSecurityConfigurerAdapter() {
  9. });
  10. webSecurity.apply(adapter);
  11. }
  12. // create Filter
  13. return webSecurity.build();
  14. }

看来Filter拦截器的配置是通过WebSecurity这个类来完成的,限于里面的代码过于复杂,本文就不展开了,感兴趣的读者可以重点关注下此类。由此可以得出Springboot的安全校验是通过过滤链的设计方式来完成的


4.URI权限校验Bean,其依赖于第三点的配置

  1. @Bean
  2. @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
  3. public WebInvocationPrivilegeEvaluator privilegeEvaluator() throws Exception {
  4. return webSecurity.getPrivilegeEvaluator();
  5. }

5.安全校验表达式验证Bean,其也依赖于第三点的配置,应该是与第四点搭配使用

  1. @Bean
  2. @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
  3. public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
  4. return webSecurity.getExpressionHandler();
  5. }

小结

Springboot整合的Security板块内容很多,本文也展示不完,不过值得关注的是以下几个方面

1)WebSecurity的个性化配置类,一般是复写抽象接口WebSecurityConfigurerAdapter,再加上@EnableWebSecurity注解便可

2)AuthenticationManagerBuilder认证校验器,重点关注其中的密码校验器,用于密码的加密解密,默认使用bcrypt方式。如果用户想通过其他数据源获取用户信息,可以关注UserDetailsService接口。推荐用户均使用AuthenticationManagerBuilder类配置认证机制!

3)WebSecurity类,此类是Springboot Security模块的核心类,具体的过滤链配置均是由此类得到的。读者以及笔者应该对此加以关注

springboot情操陶冶-web配置(八)的更多相关文章

  1. springboot情操陶冶-web配置(九)

    承接前文springboot情操陶冶-web配置(八),本文在前文的基础上深入了解下WebSecurity类的运作逻辑 WebSecurityConfigurerAdapter 在剖析WebSecur ...

  2. springboot情操陶冶-web配置(七)

    参数校验通常是OpenApi必做的操作,其会对不合法的输入做统一的校验以防止恶意的请求.本文则对参数校验这方面作下简单的分析 spring.factories 读者应该对此文件加以深刻的印象,很多sp ...

  3. springboot情操陶冶-web配置(四)

    承接前文springboot情操陶冶-web配置(三),本文将在DispatcherServlet应用的基础上谈下websocket的使用 websocket websocket的简单了解可见维基百科 ...

  4. springboot情操陶冶-web配置(二)

    承接前文springboot情操陶冶-web配置(一),在分析mvc的配置之前先了解下其默认的错误界面是如何显示的 404界面 springboot有个比较有趣的配置server.error.whit ...

  5. springboot情操陶冶-web配置(三)

    承接前文springboot情操陶冶-web配置(二),本文将在前文的基础上分析下mvc的相关应用 MVC简单例子 直接编写一个Controller层的代码,返回格式为json package com ...

  6. springboot情操陶冶-web配置(一)

    承接前文springboot情操陶冶-@SpringBootApplication注解解析,在前文讲解的基础上依次看下web方面的相关配置 环境包依赖 在pom.xml文件中引入web依赖,炒鸡简单, ...

  7. springboot情操陶冶-web配置(六)

    本文则针对数据库的连接配置作下简单的分析,方便笔者理解以及后续的查阅 栗子当先 以我们经常用的mybatis数据库持久框架来操作mysql服务为例 环境依赖 1.JDK v1.8+ 2.springb ...

  8. springboot情操陶冶-web配置(五)

    本文讲讲mvc的异常处理机制,方便查阅以及编写合理的异常响应方式 入口例子 很简单,根据之前的文章,我们只需要复写WebMvcConfigurer接口的异常添加方法即可,如下 1.创建简单的异常处理类 ...

  9. springboot情操陶冶-@SpringBootApplication注解解析

    承接前文springboot情操陶冶-@Configuration注解解析,本文将在前文的基础上对@SpringBootApplication注解作下简单的分析 @SpringBootApplicat ...

随机推荐

  1. 编程菜鸟的日记-Linux无处不在

    原文来自:http://www.linuxfederation.com/linux-everywhere Linux无处不在 “Linux无处不在.从空间站到微波炉到有Linux.”你可能听说很多以及 ...

  2. Linux 下redis 集群搭建练习

    Redis集群 学习参考:https://blog.csdn.net/jeffleo/article/details/54848428https://my.oschina.net/iyinghui/b ...

  3. 在Github上为项目添加多个用户

    点击项目目录中的Settings 点击Collaborators 添加后,合作者会受到确认邮件,等待合作者确认后,合作者就可以提交了 添加组织 在Settings中找到Organizations 点击 ...

  4. 2.SSM整合_多表_一对一或多对一的增删改查

    一对一和多对一配置一样,这里就放到一起. 1.配置文件跟上一章一样,这里就不多写了,主要是Mapper映射文件 多 接口 public interface NewsMapper { public vo ...

  5. 简述一下MVC和MVVM

    一. MVC 我们先来了解一下什么是MVC. MVC:分别所指Model.View.Controller. MVC为标准的设计模式,是官方推荐的权威的规范模式. 视图(View):用户交互界面. 控制 ...

  6. 【RL-TCPnet网络教程】第20章 RL-TCPnet之BSD Socket客户端

    第20章      RL-TCPnet之BSD Socket客户端 本章节为大家讲解RL-TCPnet的BSD Socket,学习本章节前,务必要优先学习第18章的Socket基础知识.有了这些基础知 ...

  7. 基于emWin的WAV,MP3软解软件播放器,带类似千千静听频谱,含uCOS-III和FreeRTOS两个版本

    第9期:WAV,MP3软解播放器,带类似千千静听频谱配套例子:V6-916_STemWin提高篇实验_WAV,MP3软解播放器,带类似千千静听频谱(uCOS-III)V6-917_STemWin提高篇 ...

  8. Trie树详解及其应用

    一.知识简介        最近在看字符串算法了,其中字典树.AC自动机和后缀树的应用是最广泛的了,下面将会重点介绍下这几个算法的应用.      字典树(Trie)可以保存一些字符串->值的对 ...

  9. 二叉查找树(BST)的实现

    一.二叉树介绍 二叉查找树(Binary Search Tree,BST),又称二叉排序树,也称二叉搜索树,它或者是一颗空树,或者具有如下性质的树:若它的左子树不为空,则左子树上所有节点的值都小于根节 ...

  10. 流媒体协议(二):RTMP协议

    一.概念与摘要 RTMP协议从属于应用层,被设计用来在适合的传输协议(如TCP)上复用和打包多媒体传输流(如音频.视频和互动内容).RTMP提供了一套全双工的可靠的多路复用消息服务,类似于TCP协议[ ...