安全模块设计
概述
在任何系统中,无论是小型的应用程序还是大型的网络架构,安全都是非常重要的考虑因素之一。为了保护系统和用户的安全,我们需要有一套强大的安全框架。
从最高级别的视角看,安全框架的工作可以被划分为两个核心组成部分:认证(Authentication)和授权(Authorization)。
认证(Authentication):这是首先要完成的任务,也就是确认"你是谁"。这涉及到验证用户提供的证据(如用户名和密码、数字证书、身份验证 token 等)以确定其身份。
授权(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 |
访问控制策略
基于角色的访问控制(Role-Based Access Control):基于角色的访问控制模型。在这种模型中,权限不是直接分配给个别用户,而是分配给具有一定责任和职责的角色。用户则被赋予适当的角色。RBAC有助于简化权限管理和减少错误,因为管理员只需要管理角色的权限,然后再将角色分配给用户。
访问控制列表(ACL,Access Control List):这是最简单和最常见的访问控制模型。在这种模型中,每个资源(如文件或目录)都有一个访问控制列表,指定哪些用户或用户组可以访问该资源,以及他们可以执行的操作。阿里云的 RAM(Resource Access Management)系统的确使用了类似于访问控制列表(ACL)的模型来控制对资源的访问。
自由访问控制(DAC,Discretionary Access Control):在这种模型中,资源的所有者(或者具有特定权限的用户)可以决定谁可以访问他们的资源以及可以执行的操作。这在某种程度上类似于 OAuth2 流程中的授权。然而,OAuth2 更侧重于授权第三方应用以代表用户访问资源,而不是用户直接访问资源。
强制访问控制(MAC,Mandatory Access Control):在这种模型中,访问控制策略是由中心政策决定的,个别用户不能更改。这种模型通常用于需要高级别安全性的环境,如军事或政府环境。
基于属性访问控制(ABAC,Attribute-Based Access Control):在这种模型中,不仅考虑用户的身份,还考虑其他属性,如时间、地点、资源类型等。例如,一个用户可能只在工作时间才能访问某个资源,或者只有在公司内部网络才能访问某个资源。
策略基于访问控制(PBAC,Policy-Based Access Control):在这种模型中,使用的是一组动态的、可配置的策略来决定访问控制。这些策略可以基于各种条件,如角色、属性、时间和地点等。
- 0
- 0
-
分享