《Spring Security》第六章 SpringSecurity 身份认证流程

第六章 SpringSecurity 身份认证流程

一、标准的身份认证

一个标准的身份验证流程:

  1. 用户提供用户名与密码
  2. 系统验证用户名密码是否正确
  3. 获取该用户的上下文信息(如角色列表、权限等)
  4. 为用户建立安全上下文
  5. 访问受保护资源时,通过上下文信息验证权限

以下示例来自于 Spring Security Reference,演示了一个简单的迷你认证环境。

模拟身份认证

public class AuthenticationExample {
private static AuthenticationManager am = new SampleAuthenticationManager();

public static void main(String[] args) throws Exception {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    while(true) {
        // 1. 接收用户名与密码
    System.out.println("Please enter your username:");
    String name = in.readLine();
    System.out.println("Please enter your password:");
    String password = in.readLine();
    try {
        // 2. 生成 token
        Authentication request = new UsernamePasswordAuthenticationToken(name, password);
        // 3. 验证 token 是否正确
        Authentication result = am.authenticate(request);
        // 4. 将认证主题注入上下文
        SecurityContextHolder.getContext().setAuthentication(result);
        break;
    } catch(AuthenticationException e) {
        System.out.println("Authentication failed: " + e.getMessage());
    }
    }
    System.out.println("Successfully authenticated. Security context contains: " +
            SecurityContextHolder.getContext().getAuthentication());
}
}

class SampleAuthenticationManager 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");
}
}

通常情况下,这个流程是由 Spring Security 内部执行的,但是这大致显示了 Spring Security 通过 username、password 构建一个 SecurityContext 的过程。

直接设置 SecurityContextHolder

如果你需要在一个已经拥有身份认证的系统(如自定义了过滤器或MVC控制器、拥有自己的认证系统等)中,接入 Spring Security 环境。

只需要在原有系统中读取第三方用户信息,构建一个 Spring Security 特定的 Authentication 对象,并将其放入 SecurityContextHolder 中即可。

二、Web应用中的身份验证

在一个Web 应用程序中使用 Spring Security 访问受保护的资源,流程如下:

访问资源的验证及认证流程

Spring Security 中提供了不同的类负责上图中的不同流程,主要参与者(按照使用顺序)是 ExceptionTranslationFilter、AuthenticationEntryPoint和 authentication mechanism。负责调用的是 AuthenticationManager。

安全拦截器与安全对象模型

ExceptionTranslationFilter

ExceptionTranslationFilter 是一个 Spring Security 过滤器,负责检测所有引发的 Spring Security 异常。通常情况下,异常都是由 AbstractSecurityInterceptor 引发的,它是授权服务的主要提供者。

ExceptionTranslationFilter 在验证主题时(如图中的 ③⑦),负责返回错误代码403(即认证成功,但是缺少权限的情况),或者启动 AuthenticationEntryPoint(尚未登录)。

AuthenticationEntryPoint

负责为 web 应用程序提供一个默认的身份认证策略(③ )。

Authentication Mechanism 认证机制

浏览器提供了身份验证凭据之后,服务器就需要收集这些身份信息,并进入“身份验证机制”。将用户验证的凭据生成一个“request”对象,然后交给 AuthenticationManager。

如果 AuthenticationManager 验证接收回完全填充的 Authentication 对象后,它将认为请求有效,并将 Authentication 放入 SecurityContextHolder 中,并进行重试请求。

如果 AuthenticationManager 拒绝请求,身份验证机制将要求用户重新进行认证(跳转至url或者返回http状态码)。

Store SecurityContext

在 Spring Security 中,由 SecurityContextPersistenceFilter 来承担在请求之间存储 SecurityContext,它默认将 SecurityContext 作为HttpSession 的属性存储。

每个请求都会将 SecurityContext 恢复到 SecurityContextHolder 汇总,并且在请求完成时清除 SecurityContextHolder。这样做是十分安全的,且您不应该直接与 HttpSession交互,而应当总是与 SecurityContextHolder 交互。

如果在不使用 HttpSession 的应用程序(如 无状态的 Restful web 服务)中,对每个请求进行身份验证。SecurityContextPersistenceFilter 依然十分重要,因为它会确保 SecurityContextHolder 在每个请求之后被清除。

您可以通过自定义 SecurityContextPersistenceFilter 的行为,为每个请求创建一个新的 SecurityContext。以防止一个线程中的更改影响另一个线程,或者在临时更改上下文的地方创建一个新实例。

Access-Control 访问控制

在 Spring Security 中负责访问控制决策的接口是 AccessDecisionManager,它具有一个 decide 方法,该方法使用 Authentication 对象代表请求访问的主体、安全对象、和一个基于该对象的安全元数据属性列表(如角色列表属性等)。

Spring Security 与 AOP

AOP 中提供了如:before、after、throws、around通知。Spring Security 为方法调用和 Web 请求提供了一个大致的建议,通过 around 通知,可以决定该方法是否继续调用,是否修改响应,以及是否抛出异常。

文章作者: koral
文章链接: http://luokaiii.github.io/2019/07/18/读书笔记/《SpringSecurity》/6.身份认证流程/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自