Spring Security学习与配置

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

Spring Security核心类的介绍

Authentication

    Authentication是一个接口,用来表示用户认证信息的,在用户登录认证之前,相应信息会封装为一个Authentication具体实现类的对象,在登录认证成功之后又会生成一个信息更全面,包含用户权限等信息的Authentication对象,然后把该对象保存在SecurityContextHolder所持有的SecurityContext中,供后续的程序进行调用,如访问权限的鉴定等。


SecurityContextHolder

      SecurityContextHolder是用来保存SecurityContext的。SecurityContext中含有当前正在访问系统的用户的详细信息。默认情况下,SecurityContextHolder将使用ThreadLocal来保存SecurityContext,这也就意味着在处于同一线程中的方法中我们可以从ThreadLocal中获取到当前的SecurityContext。因为线程池的原因,如果我们每次在请求完成后都将ThreadLocal进行清除的话,那么我们把SecurityContext存放在ThreadLocal中还是比较安全的。这些工作Spring Security已经自动为我们做了,即在每一次request结束后都将清除当前线程的ThreadLocal。

       Spring Security使用一个Authentication对象来描述当前用户的相关信息。SecurityContextHolder中持有的是当前用户的SecurityContext,而SecurityContext持有的是代表当前用户相关信息的Authentication的引用。这个Authentication对象不需要我们自己去创建,在与系统交互的过程中,Spring Security会自动为我们创建相应的Authentication对象,然后赋值给当前的SecurityContext。

AuthenticationManager和AuthenticationProvider

      AuthenticationManager是一个用来处理认证(Authentication)请求的接口。在其中只定义了一个方法authenticate(),该方法只接收一个代表认证请求的Authentication对象作为参数,如果认证成功,则会返回一个封装了当前用户权限等信息的Authentication对象进行返回。

  Spring Security中,AuthenticationManager的默认实现是ProviderManager,而且它不直接自己处理认证请求,而是委托给其所配置的AuthenticationProvider列表,然后会依次使用每一个AuthenticationProvider进行认证,如果有一个AuthenticationProvider认证后的结果为null,则表示该AuthenticationProvider已经认证成功,之后的AuthenticationProvider将不再继续认证。然后直接以该AuthenticationProvider的认证结果作为ProviderManager的认证结果。如果所有的AuthenticationProvider的认证结果都为null,则表示认证失败,将抛出一个异常。校验认证请求最常用的方法是根据请求的用户名加载对应的UserDetails,然后比对UserDetails的密码与认证请求的密码是否一致,一致则表示认证通过。Spring Security内部的DaoAuthenticationProvider就是使用的这种方式。其内部使用UserDetailsService来负责加载UserDetails。在认证成功以后会使用加载的UserDetails来封装要返回的Authentication对象,加载的UserDetails对象是包含用户权限等信息的。认证成功返回的Authentication对象将会保存在当前的SecurityContext中。默认情况下,在认证成功后ProviderManager将清除返回的Authentication中的凭证信息,如密码。

UserDetailsService

    通过Authentication.getPrincipal()的返回类型是Object,但很多情况下其返回的其实是一个UserDetails的实例。UserDetails是Spring Security中一个核心的接口。其中定义了一些可以获取用户名、密码、权限等与认证相关的信息的方法。Spring Security内部使用的UserDetails实现类大都是内置的User类,我们如果要使用UserDetails时也可以直接使用该类。在Spring Security内部很多地方需要使用用户信息的时候基本上都是使用的UserDetails,比如在登录认证的时候。登录认证的时候Spring Security会通过UserDetailsService的loadUserByUsername()方法获取对应的UserDetails进行认证,认证通过后会将该UserDetails赋给认证通过的Authentication的principal,然后再把该Authentication存入到SecurityContext中。之后如果需要使用用户信息的时候就是通过SecurityContextHolder获取存放在SecurityContext中的Authentication的principal。

    通常我们需要在应用中获取当前用户的其它信息,如Email、电话等。这时存放在Authentication的principal中只包含有认证相关信息的UserDetails对象可能就不能满足我们的要求了。这时我们可以实现自己的UserDetails,在该实现类中我们可以定义一些获取用户其它信息的方法,这样将来我们就可以直接从当前SecurityContext的Authentication的principal中获取这些信息了。上文已经提到了UserDetails是通过UserDetailsService的loadUserByUsername()方法进行加载的。UserDetailsService也是一个接口,我们也需要实现自己的UserDetailsService来加载我们自定义的UserDetails信息。然后把它指定给AuthenticationProvider即可。下面给出一个配置示例

  <!-- 用于认证的AuthenticationManager -->
<security:authentication-manager alias="authenticationManager">
      <security:authentication-provider
         user-service-ref="userDetailsService" />
   </security:authentication-manager> 
  <bean id="userDetailsService"
     class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
      <property name="dataSource" ref="dataSource" />
   </bean>
      上述代码中我们使用的JdbcDaoImplSpring Security为我们提供的UserDetailsService的实现,另外Spring Security还为我们提供了UserDetailsService另外一个实现,InMemoryDaoImpl。

JdbcDaoImpl和InMemoryDaoImpl

  JdbcDaoImpl允许我们从数据库来加载UserDetails,其底层使用的是SpringJdbcTemplate进行操作,所以我们需要给其指定一个数据源

InMemoryDaoImpl主要是测试用的,其只是简单的将用户信息保存在内存中。使用NameSpace时,使用user-service元素Spring Security底层使用的UserDetailsService就是InMemoryDaoImpl。此时,我们可以简单的使用user元素来定义一个UserDetails

   <security:user-service>
      <security:user name="user" password="user" authorities="ROLE_USER"/>
   </security:user-service>
GrantedAuthority

   Authentication的getAuthorities()可以返回当前Authentication对象拥有的权限,即当前用户拥有的权限。其返回值是一个GrantedAuthority类型的数组,每一个GrantedAuthority对象代表赋予给当前用户的一种权限。GrantedAuthority是一个接口,其通常是通过UserDetailsService进行加载,然后赋予给UserDetails的。

   GrantedAuthority中只定义了一个getAuthority()方法,该方法返回一个字符串,表示对应权限的字符串表示,如果对应权限不能用字符串表示,则应当返回null。

   Spring Security针对GrantedAuthority有一个简单实现SimpleGrantedAuthority。该类只是简单的接收一个表示权限的字符串。Spring Security内部的所有AuthenticationProvider都是使用SimpleGrantedAuthority来封装Authentication对象。


认证过程

用户使用用户名和密码进行登录。

       2、Spring Security将获取到的用户名和密码封装成一个实现了Authentication接口的UsernamePasswordAuthenticationToken。

       3、将上述产生的token对象传递给AuthenticationManager进行登录认证。

       4、AuthenticationManager认证成功后将会返回一个封装了用户权限等信息的Authentication对象。

       5、通过调用SecurityContextHolder.getContext().setAuthentication(...)将AuthenticationManager返回的Authentication对象赋予给当前的SecurityContext。

       上述介绍的就是Spring Security的认证过程。在认证成功后,用户就可以继续操作去访问其它受保护的资源了,但是在访问的时候将会使用保存在SecurityContext中的Authentication对象进行相关的权限鉴定。

WEB认证过程

    如果用户直接访问登录页面,那么认证过程跟上节描述的基本一致,只是在认证完成后将跳转到指定的成功页面,默认是应用的根路径。如果用户直接访问一个受保护的资源,那么认证过程将如下:

       1、引导用户进行登录,通常是重定向到一个基于form表单进行登录的页面,具体视配置而定。

       2、用户输入用户名和密码后请求认证,后台还是会像上节描述的那样获取用户名和密码封装成一个UsernamePasswordAuthenticationToken对象,然后把它传递给AuthenticationManager进行认证。

       3、如果认证失败将继续执行步骤1,如果认证成功则会保存返回的Authentication到SecurityContext,然后默认会将用户重定向到之前访问的页面。

       4、用户登录认证成功后再次访问之前受保护的资源时就会对用户进行权限鉴定,如不存在对应的访问权限,则会返回403错误码。


ExceptionTranslationFilter

    ExceptionTranslationFilter 是用来处理来自AbstractSecurityInterceptor 抛出的AuthenticationException AccessDeniedException 的。AbstractSecurityInterceptor Spring Security 用于拦截请求进行权限鉴定的,其拥有两个具体的子类,拦截方法调用的MethodSecurityInterceptor 和拦截URL 请求的FilterSecurityInterceptor 。当ExceptionTranslationFilter 捕获到的是AuthenticationException 时将调用AuthenticationEntryPoint 引导用户进行登录;如果捕获的是AccessDeniedException ,但是用户还没有通过认证,则调用AuthenticationEntryPoint 引导用户进行登录认证,否则将返回一个表示不存在对应权限的403 错误码。

在request之间共享SecurityContext

   可能你早就有这么一个疑问了,既然 SecurityContext是存放在ThreadLocal中的,而且在每次权限鉴定的时候都是从ThreadLocal中获取SecurityContext中对应的Authentication所拥有的权限,并且不同的request是不同的线程,为什么每次都可以从ThreadLocal中获取到当前用户对应的SecurityContext呢?在Web应用中这是通过SecurityContextPersistentFilter实现的,默认情况下其会在每次请求开始的时候从session中获取SecurityContext,然后把它设置给SecurityContextHolder,在请求结束后又会将SecurityContextHolder所持有的SecurityContext保存在session中,并且清除SecurityContextHolder所持有的SecurityContext。这样当我们第一次访问系统的时候,SecurityContextHolder所持有的SecurityContext肯定是空的,待我们登录成功后,SecurityContextHolder所持有的SecurityContext就不是空的了,且包含有认证成功的Authentication对象,待请求结束后我们就会将SecurityContext存在session中,等到下次请求的时候就可以从session中获取到该SecurityContext并把它赋予给SecurityContextHolder了,由于SecurityContextHolder已经持有认证过的Authentication对象了,所以下次访问的时候也就不再需要进行登录认证了。



    

       

       



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!对于学习Spring Security,您可以按照以下步骤进行: 1. 了解基本概念:首先,了解Spring Security是什么以及它的作用。它是一个强大且灵活的身份验证和授权框架,用于保护Java应用程序的安全性。 2. 配置环境:确保您在开发环境中安装了Java和Spring框架。您可以使用Maven或Gradle构建工具来管理依赖项。 3. 添加Spring Security依赖:在项目的构建文件中添加Spring Security依赖项。根据您使用的构建工具,可以在Maven的pom.xml文件或Gradle的build.gradle文件中添加相应的依赖。 4. 配置Spring Security:通过创建一个继承自`WebSecurityConfigurerAdapter`的配置类,来配置Spring Security。您可以在这个类中定义身份验证和授权规则,以及其他安全相关的设置。 5. 自定义用户认证:您可以选择使用内存、数据库或LDAP等不同的用户存储方式。通过实现`UserDetailsService`接口,您可以自定义用户认证逻辑。 6. 定义安全规则:在配置类中,您可以定义访问URL的权限要求、角色等安全规则。您可以使用`antMatchers()`方法来指定URL模式,并使用`hasRole()`等方法来定义所需的角色。 7. 使用表单登录:Spring Security提供了表单登录的支持。您可以配置登录页面的URL、用户名和密码参数名等。还可以自定义登录成功和失败的处理逻辑。 8. 添加注解:通过使用`@EnableWebSecurity`注解,您可以启用Spring Security,并将其集成到您的应用程序中。此外,您还可以使用`@PreAuthorize`和`@Secured`等注解来添加方法级别的安全性。 9. 测试和调试:编写单元测试来验证您的安全配置是否按预期工作。使用不同的角色和权限进行测试,确保只有授权的用户能够访问受限资源。 10. 持续学习和实践:Spring Security是一个庞大而复杂的框架,有很多功能和扩展。继续阅读官方文档、教程和示例代码,不断实践和探索。 希望这些步骤能够帮助您开始学习Spring Security!如果您有任何进一步的问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值