yuanzhixiang's blog

yuanzhixiang

安全模块设计

26
2023-06-13

概述

在任何系统中,无论是小型的应用程序还是大型的网络架构,安全都是非常重要的考虑因素之一。为了保护系统和用户的安全,我们需要有一套强大的安全框架。

从最高级别的视角看,安全框架的工作可以被划分为两个核心组成部分:认证(Authentication)和授权(Authorization)。

  1. 认证(Authentication):这是首先要完成的任务,也就是确认"你是谁"。这涉及到验证用户提供的证据(如用户名和密码、数字证书、身份验证 token 等)以确定其身份。

  2. 授权(Authorization):在确定了"你是谁"之后,下一步就是确定"你可以做什么"。这就涉及到对用户的权限进行管理和检查,以确保他们只能访问他们应当访问的资源,并且只能执行他们被允许执行的操作。

案例分析

安全框架的基本需求是通用的,也就是验证用户身份并控制用户访问权限,因此这些框架在设计上存在共通之处。这些框架都以身份认证和授权作为核心功能,通过一系列组件和接口实现这两个功能。通过学习和理解这些开源框架的设计和实现,我们可以吸取它们的优点,避免它们的缺点,从而帮助我们设计和实现更好的自定义安全框架。同时,这也可以提高我们对常见安全问题和解决方案的理解,从而提高我们的系统安全性。

Apache Shiro

介绍

Apache Shiro 是一个 Java 的安全框架,提供了身份验证、授权、加密和会话管理功能,适用于任何类型的 Java 应用程序,从最小的命令行应用程序到最大的网络和企业级应用程序。

认证和授权

在 Apache Shiro 中,身份验证和授权都是通过 Subject 这个核心概念来处理的。Subject 代表了当前的用户或主体,可以通过 Subject 实例进行登录、登出、角色检查、权限验证等操作。

核心概念

  • Subject:代表了当前的用户,通过它可以进行身份验证和授权的操作。

  • Realm:用于获取安全实体的验证信息,是连接安全实体与安全服务的桥梁。

  • Authenticator: 负责主体/用户的认证,是一个核心的认证控制器。

  • Authorizer: 负责授权或访问控制的行为,是一个核心的授权控制器.

  • SecurityManager:在 Shiro 中是安全操作的核心,负责与后续的所有安全相关的操作进行交互。

/**
 * 负责在应用程序中进行身份认证
 */
public interface Authenticator {

    /**
     * 对提交的 AuthenticationToken 对用户进行认证。如果认证成功,则返回一个
     * 表示用户在 Shiro 中相关账户数据的 AuthenticationInfo 实例。
     */
    public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException;
}

/**
 * 认证过程中提交的凭据
 */
public interface AuthenticationToken extends Serializable {

    /**
     * 在用户名和密码的身份验证请求中,这是用户名。
     */
    Object getPrincipal();

    /**
     * 在用户名和密码的身份验证请求中,这是密码。
     */
    Object getCredentials();

}

/**
 * 身份认证成功后返回的认证信息
 */
public interface AuthenticationInfo extends Serializable {

    /**
     * 返回用户信息如用户名、用户ID等
     */
    PrincipalCollection getPrincipals();

    /**
     * 认证凭据,如密码、私钥
     */
    Object getCredentials();

}

Spring Security

介绍

Spring Security 是一个用于 Java 应用程序的强大且高度可定制的身份验证和访问控制框架。它主要关注提供身份验证和授权服务的方法,同时提供了防止各种攻击的措施,如跨站请求伪造(CSRF)。

认证和授权

在 Spring Security 中,身份认证(Authentication)是通过 AuthenticationManager 接口来处理的,授权(Authorization)则由 AuthorizationManager 接口处理。 AuthenticationManager 负责验证用户提供的证据(如用户名和密码),然后决定用户是否被允许登录。AuthorizationManager 则是基于用户的角色和权限信息,以及资源的安全配置,来决定用户对资源的访问权限。

核心概念

  • Authentication:提供了关于主体的基本信息,如主体的名称,证明主体身份的证书,以及主体是否已经被认证的信息。Authentication 对象可以包含主体以及其权限集合,这些权限表示了经过身份验证的主体被授予了哪些权限。

  • AuthenticationManager: 其实现类负责验证 Authentication。最常用的实现类是 ProviderManager,它管理一系列的 AuthenticationProvider。

  • AuthenticationProvider:定义了如何验证 Authentication。它有许多实现类,可以对不同类型的 Authentication 进行处理,例如 DaoAuthenticationProvider 会使用 UserDetailsService 来获取用户详细信息并进行身份验证。

  • AuthorizationManager: 用来做授权决策的核心接口。它的主要任务是基于当前的认证信息(Authentication 对象),决定是否允许对一个具体的对象(如 URL、方法等)进行访问。

  • UserDetailsService: 定义了如何根据用户名获取用户详情。最常用的 UserDetails 实现是 User,它保存了用户的用户名、密码和权限信息。

public interface AuthenticationManager {
    /**
     * 尝试对传入的 {@link Authentication} 对象进行认证,如果成功,
     * 则返回一个完全填充的 <code>Authentication</code> 对象(包括授予的权限)。
     * 
     * @param authentication 认证请求对象
     * @return 一个完全认证的对象,包括凭据
     * @throws AuthenticationException 如果认证失败
     */
    Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

public interface AuthenticationProvider {

    /**
     * 认证类的真正实现
     */
    Authentication authenticate(Authentication authentication) throws AuthenticationException;

    /**
     * 是否可对该类进行认证
     */
    boolean supports(Class<?> authentication);

}

/**
 * 加载用户特定数据的核心接口。
 *
 * @author Ben Alex
 * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider
 * @see UserDetails
 */
public interface UserDetailsService {

    /**
     * 根据用户名定位用户。在实际实现中,搜索可能是区分大小写的,
     * 或者根据实现实例的配置而不区分大小写。在这种情况下,返回的
     * <code>UserDetails</code> 对象的用户名可能与实际请求的大小写不同。
     * @param username 需要其数据的用户的用户名。
     * @return 一个完全填充的用户记录(永远不会是 <code>null</code>)
     * @throws UsernameNotFoundException 如果找不到用户或者用户没有 GrantedAuthority
     */
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

}

/**
 * 一个能够判断 {@link Authentication} 是否可以访问特定对象的授权管理器。
 *
 * @param <T> 授权检查所涉及的对象的类型。
 * @author Evgeniy Cheban
 */
@FunctionalInterface
public interface AuthorizationManager<T> {

    /**
     * 决定是否应为特定的身份验证和对象授予权限。
     * @param authentication 要检查的 {@link Authentication} 的 {@link Supplier}
     * @param object 要检查的 {@link T} 对象
     * @throws AccessDeniedException 如果未授予权限
     */
    default void verify(Supplier<Authentication> authentication, T object) {
        AuthorizationDecision decision = check(authentication, object);
        if (decision != null && !decision.isGranted()) {
                throw new AccessDeniedException("Access Denied");
        }
    }

    /**
     * 决定是否为特定的身份验证和对象授予权限。
     * @param authentication 要检查的 {@link Authentication} 的 {@link Supplier}
     * @param object 要检查的 {@link T} 对象
     * @return 一个 {@link AuthorizationDecision},如果无法做出决定,则返回 null
     */
    @Nullable
    AuthorizationDecision check(Supplier<Authentication> authentication, T object);

}

概念比较

功能 / 框架

Spring Security

Apache Shiro

认证器

AuthenticationManager

Authenticator

认证提供器

AuthenticationProvider

Realm

授权器

AuthorizationManager

Authorizer

安全管理器

AuthenticationManager & AuthorizationManager

SecurityManager

数据源

UserDetailsService

Realm

认证的主体

Authentication

AuthenticationToken

访问控制策略

  1. 基于角色的访问控制(Role-Based Access Control):基于角色的访问控制模型。在这种模型中,权限不是直接分配给个别用户,而是分配给具有一定责任和职责的角色。用户则被赋予适当的角色。RBAC有助于简化权限管理和减少错误,因为管理员只需要管理角色的权限,然后再将角色分配给用户。

  2. 访问控制列表(ACL,Access Control List):这是最简单和最常见的访问控制模型。在这种模型中,每个资源(如文件或目录)都有一个访问控制列表,指定哪些用户或用户组可以访问该资源,以及他们可以执行的操作。阿里云的 RAM(Resource Access Management)系统的确使用了类似于访问控制列表(ACL)的模型来控制对资源的访问。

  3. 自由访问控制(DAC,Discretionary Access Control):在这种模型中,资源的所有者(或者具有特定权限的用户)可以决定谁可以访问他们的资源以及可以执行的操作。这在某种程度上类似于 OAuth2 流程中的授权。然而,OAuth2 更侧重于授权第三方应用以代表用户访问资源,而不是用户直接访问资源。

  4. 强制访问控制(MAC,Mandatory Access Control):在这种模型中,访问控制策略是由中心政策决定的,个别用户不能更改。这种模型通常用于需要高级别安全性的环境,如军事或政府环境。

  5. 基于属性访问控制(ABAC,Attribute-Based Access Control):在这种模型中,不仅考虑用户的身份,还考虑其他属性,如时间、地点、资源类型等。例如,一个用户可能只在工作时间才能访问某个资源,或者只有在公司内部网络才能访问某个资源。

  6. 策略基于访问控制(PBAC,Policy-Based Access Control):在这种模型中,使用的是一组动态的、可配置的策略来决定访问控制。这些策略可以基于各种条件,如角色、属性、时间和地点等。