Spring Security:核心组件说明

理解了Spring Security核心组件说明,Spring Security的后续学习和使用将不再是难题。

1、Spring Security 核心

SecurityContextHolder、SecurityContext、Authentication、GrantedAuthority、UserDetails

Authentication:代表了Spring Security中的当事人。

SecurityContext:拥有了Authentication、请求相关的信息。

SecurityContextHodler:用于获取SecurityContext。

GrantedAuthority:代表在应用程序中给当事人授予的权限。

UserDetails:用户详细信息。其实就是一个JavaBean。

UserDetailsService:UserDetails相关的业务处理。

这几个是Spring Security的核心,其它的API都是围绕这些API展开的,都是为它们服务的。

2、身份认证Authentication

2.1、一般的身份认证

从Spring Security核心部分,对Spring Security有一个笼统概念了,那该怎么理解上面说的呢?

通常情况下,我们的系统都是这样的:

1、用户输入用户名、密码登录

2、系统对用户名、密码进行验证

3、获取用户上下文信息(角色列表等等)

4、获取相关操作权限

对于上面说的前三条,用Spring Security来处理,就是:

1、用户名、密码组合生成一个Authentication对象(也就是UsernamePasswordAuthenticationToken对象)。

2、生成的这个token对象会传递给一个AuthenticationManager对象用于验证。

3、当成功认证后,AuthenticationManager返回一个Authentication对象。

4、接下来,就可以调用

SecurityContextHodler.getContext().setAuthentication(…)。

为了更好的理解,下面就写一个例子:

package com.springsecurity.java.test;

import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.util.ArrayList;

import java.util.List;

import org.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.authentication.BadCredentialsException;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.AuthenticationException;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.context.SecurityContextHolder;

public class AuthenticationExample {

   private static SimpleAuthenticationManager samgr = new SimpleAuthenticationManager();

   public static void main(String[] args) {

      try {

// 用户输入用户名、密码:

        BufferedReader in = new BufferedReader(new InputStreamReader(

              System.in));

        System.out.println("Please enter your username:");

        String name = in.readLine();

        System.out.println("Please enter your password:");

        String password = in.readLine();

// 接下来是系统进行身份认证的过程:

//1、将用户名、密码封装成一个token

        Authentication token = new UsernamePasswordAuthenticationToken(

              name, password);

//2、将token传给AuthenticationManager进行身份认证

//3、认证完毕,返回一个认证后的身份:

        Authentication result = samgr.authenticate(token);

// 认证后,存储到SecurityContext里 :    SecurityContextHolder.getContext().setAuthentication(result);

      } catch (Exception ex) {

        System.out.println("认证失败");

      }

// 从SecurityContext读取认证的身份:

System.out.println(SecurityContextHolder.getContext()

           .getAuthentication());

   }

}

class SimpleAuthenticationManager implements AuthenticationManager {

   static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();

   static {

      AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));

   }

   public Authentication authenticate(Authentication auth)

        throws AuthenticationException {

      if (auth.getName().equals(auth.getCredentials())) {

        return new UsernamePasswordAuthenticationToken(auth.getName(),

              auth.getCredentials(),AUTHORITIES);

      }

      throw new BadCredentialsException("Bad Credentials");

   }

}

2.2、Web Application中如何进行身份认证呢?

1、用户在首页上点击某个链接

2、后台处理时,先判断是否是要访问一个受保护的资源

3、如果是受保护的资源,判断用户是否登录,是否有这个资源的访问权限

4、如果用户没有登录,返回一个登录页面给用户

5、用户输入username、password,然后登录

6、接下来开始上面的身份认证过程

在Web Application环境下,将上述1-4过程由AuthenticationEntryPoint来处理。

如何存储已认证的用户呢?

用户要访问另外的资源时,肯定要判断是否有该资源的访问权限,在判断是否有访问权限前,一般要用户先登录系统(要对用户的身份进行认证)的。如果用户已经成功登录,只需要判断是否有访问权即可。

在一般的Web Application中(不使用Spring Security),我们通常会将用户信息存储到HttpSession中。

如果在Web Application系统中,加上了Spring Security呢?

做法没什么两样,还是将这个信息存储在HttpSession中。但对于无状态的RESTful Web Service不是这样做的。

3、授权Authorization(Access Control)

身份认证Authentication保证了用户可访问系统。权限认证(授权保证了用户可以访问系统中的资源)。

在用户的一次资源访问中,这两个过程是不可少的。Access Control就是决定你这个请求是否被允许,它是在身份认证之后,访问资源之前进行的。


用户请求-->身份认证-->授权-->资源访问-->响应

身份认证过程由AuthenticationManager来处理,授权决定则是由AccessDecisionManager来处理的。

void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException;

Spring在进行授权时,充分利用了Spring的核心之一:AOP。通过前段时间关于AOP的研究,AOP在程序中一般体现为Filter或者是Interceptor,其中更多的是使用Interceptor。

上面的decide方法需要三个参数:

·Authentication就是已经通过认证的Authentication对象。上面的学习已经可以理解。

·Object obj  是代表方法调用(MethodInvocation)或者请求处理(Action的Handler),在接下来的AbstractSecurityInterceptor中说明。

·configAttributes则是相关的特性配置。下面会有说明。

AbstractSecurityInterceptor

Security中关于授权部分使用了AOP,所以就不得不了解一下AbstractSecurityInterceptor。

Interceptor一般都会提供invoke方法。这个类是抽象类,没有提供,使用时用的是它的子类,在子类中提供了invoke方法。而在AccessDecisionManager的decide方法中的第二个参数,就是invoke方法的参数MethodInvocation对象。

ConfigAttribute

其实就是对某个拦截器配置一些访问属性。举个例子:

某个拦截器interceptor,配置访问属性有ROLE_A, ROLE_B,如果一个用户经过认证后他有一个Authentication为ROLE_A,那么他的这个请求就会被拦截器interceptor处理。

再说的通俗点就是:设置某个拦截器能够处理哪些身份的用户的请求。

AbstractSecurityInterceptor的执行流程

1、查找有哪些configAttributes与当前的请求相关联。

2、提交secure object(就是前面说的MethodInvocation对象)、当前的Authentication(就是已经认证好的身份)、以及1中找到的那些configAttributes,提交给AccessDecisionManager用于授权。

3、选择性的改变用户的身份去进行验证。这是因为用户身份的多样性的需要。

4、Secure Object(methodInvocation 对象执行),也就是执行我们在action中写的Handler。

5、如果配置了AfterInvocationManager,那么AfterInvocationManager也会执行的。

这个流程是官方文档中说明的,已经很清楚了。为了更清新的了解这个过程,还是查看一下源码吧:

在AbstractSecurityInterceptor的子类MethodSecurityInterceptor中:

public Object invoke(MethodInvocation mi) throws Throwable {

// 预先处理

        InterceptorStatusToken token = super.beforeInvocation(mi);

        Object result;

        try {

// 真实方法调用,也就是我们写的action调用

            result = mi.proceed();

        } finally {

            super.finallyInvocation(token);

        }

// afterInvocationManager处理

        return super.afterInvocation(token, result);

    }

这段代码与上面的流程说明对应起来,那就应该是上面说的流程中的1、2、3对应了这段代码中的 预先处理部分。就来看看beforeInvocation:

// 参数object就是方法调用

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());

        }

// 收集与方法调用相关的特性配置

        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);

        }

// 取得该用户已验证的身份

        Authentication authenticated = authenticateIfRequired();

        // Attempt authorization

        try {

// 进行用户授权

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));

        }

// 切换为其他身份

        // Attempt to run as a different user

        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);

        }

    }

4、Localization异常消息的国际化

Spring Security分为了身份认证和授权两个过程,上面已经讲明 。在这两个过程中,发生异常是在所难免的。Spring为此提供了异常消息的国际化支持。

时间: 2024-08-30 01:31:52

Spring Security:核心组件说明的相关文章

Spring Security 入门原理及实战

参考博客: https://www.cnblogs.com/demingblog/p/10874753.html Spring Security 是spring项目之中的一个安全模块,可以非常方便与spring项目无缝集成. 特别是在spring boot项目中加入spring security更是十分简单, 这里就简单介绍一下入门案例和使用原理吧! 写一个简单的springboot测试例子: @Controller public class AppController { @RequestMa

spring security 原理+实战

疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 疯狂创客圈 高并发 环境 视频,陆续上线: Windows Redis 安装(带视频) Linux Redis 安装(带视频) Windows Zookeeper 安装(带视频) Linux Zookeeper 安装(带视频) RabbitMQ 离线安装(带视频) Nacos 安装(带视频) ElasticSear

&lt;Web&gt; spring security翻译

第一步就是创建java配置,配置就是创建一个Servlet Filter,也就是springSecurityFilterChain,它负责处理应用中的所有spring security事物(保护url,验证用户名密码,重定向到登录表单).configureGlobal方法名不重要,重要的是需要在有@EnableWebSecurity.@EnableGlobalMethodSecurity.@EnableGlobalAuthentication等注解的类下配置AuthenticationManag

Spring Boot整合Spring Security

Spring Boot对于该家族的框架支持良好,但是当中本人作为小白配置还是有一点点的小问题,这里分享一下.这个项目是使用之前发布的Spring Boot会员管理系统重新改装,将之前filter登录验证改为Spring Security 1. 配置依赖 Spring Boot框架整合Spring Security只需要添加相应的依赖即可,其后都是配置Spring Security. 这里使用Maven开发,依赖如下: <dependency> <groupId>org.spring

Spring security基本使用

Spring security 学习记录 1.Spring security 简介 ? Spring Security 为 Java EE-based 企业软件应用程序提供全面的安全服务(也就是用户登录页面和相关权限的控制),应用的安全性包括用户认证( Authentication )和用户权限( Authorization )两部分. 用户认证是确定某个用户是否有进入系统的权限,使用用户名密码去认证,也就是所谓的登录:用户权限是确定哪些用户有哪些功能权限,一般都是按角色. 2.主要过滤器 ?

Spring Security入门Demo

一.spring Security简介 SpringSecurity,这是一种基于Spring AOP和Servlet过滤器的安全框架.它提供全面的安全性解决方案,同时在Web请求级和方法调用级处理身份确认和授权.在Spring Framework基础上,Spring Security充分利用了依赖注入(DI,Dependency Injection)和面向切面技术. 二.建立工程 参考http://blog.csdn.net/haishu_zheng/article/details/51490

CAS 与 Spring Security 3整合配置详解

一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分.用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统.用户授权指的是验证某个用户是否有权限执行某个操作.在一个系统中,不同用户所具有的权限是不同的.比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改.一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限. 对于上面提到的两种应用情景,Spring Security 框

spring security+mybatis+springMVC构建一个简单的项目

1.引用 spring security ,这是一种基于spring AOP和Servlet的过滤安全框架.它提供全面的安全性解决方案,同时在web请求级和方法的调用级处理身份确认和授权.在spring framework基础上,spring security充分利用了依赖注入(DI,Dependency Injection)和AOP技术. 下面就让我们用一个小的晓得项目来出初步了解Spring Security 的强大功能吧. 2.项目实战    1)项目的技术架构:maven+spring

Spring Security视频地址

1:Spring Security视频 附件为txt文档内含百度云盘的链接,由于视频太大,所以只能分享链接了..... http://pan.baidu.com/share/link?shareid=2726555995&uk=706734182  提取码:60tb 2:Spring Securoty: 链接:http://pan.baidu.com/s/1o6x2sye 密码:fi2x 3:链接: http://pan.baidu.com/s/1pJnylQF 密码: wj6b 4:http: