前言
上一文中,留了一些坑,本文我们将从登录认证概念本身入手。
认证与授权基础概念
认证(Authentication)和授权(Authorization)是计算机安全方面的两个基础概念
认证(Authentication)
认证是验证用户身份的过程,即向系统证明用户是谁。这通常涉及到用户提供一些凭据,如用户名和密码,或者更复杂的认证方式,如双因素认证或生物识别认证。系统会对这些凭据进行验证,以确认用户的身份是否真实。如果凭据有效,用户就被认证为系统的合法用户。
认证是确保只有合法用户可以访问系统或资源的关键步骤,它是计算机领域安全性的基石之一。通过认证,系统可以防止未经授权的访问,保护敏感数据和功能不被非法访问。
- 常见的认证实现
- 用户名和密码认证:这是最基本的认证方式,用户需要提供注册时设定的用户名和密码,系统将验证用户名和密码的匹配性来确认用户身份。
- 多因素认证(MFA):为了提高安全性,很多系统采用多因素认证,要求用户提供两种或更多种类型的凭证,例如密码加上指纹识别、动态令牌等。
- 单点登录(SSO):单点登录允许用户在一个中心认证服务器上进行一次身份验证后,无需再次输入用户名和密码即可访问多个相关联的应用系统。
- OAuth认证:OAuth是一种开放标准,用于授权第三方应用访问用户在其他服务上存储的私有资源,而无需将用户名和密码提供给第三方应用,常见的使用场景是社交媒体的登录。
- OpenID Connect认证:OpenID Connect是基于OAuth 2.0协议的身份认证层,提供了一种跨多个网站和应用的标准化方式来认证用户。
- 基于证书的认证:在这种方法中,用户或设备必须提供一个有效的数字证书来证明其身份,证书包含用户的公钥和一些标识信息,并由受信任的证书颁发机构(CA)签发。
- 基于令牌的认证:如JSON Web Tokens(JWTs),这是一种开放标准,定义了一种紧凑的、自包含的方式,用于在各方之间安全地传输信息,这些信息可以被验证和信任。
- HTTP Basic认证:HTTP Basic登录验证模式是Spring Security实现登录验证最简单的一种方式,它的目的并不是保障登录验证的绝对安全,而是提供一种简单的登录验证。
这些认证方式各有特点,适用于不同的场景和需求。在实际应用中,可以根据系统的安全需求和用户体验要求来选择合适的认证方式。同时,为了保证系统的安全性,建议定期更新和升级认证方式,以应对新的安全威胁和漏洞。
Spring Security支持如下认证方式:
- HTTP Basic认证:这是最简单的认证方式之一,其中用户的凭据(用户名和密码)以Base64编码格式在HTTP请求头中发送。它主要适用于RESTful Web服务。
- HTTP Digest认证:与Basic认证相比,Digest认证提供了更高的安全性,因为它使用摘要算法来传输凭据,而不是明文。
- 表单认证:这是Web应用程序中最常见的认证方式,其中用户通过表单输入用户名和密码,然后这些信息被发送到服务器进行验证。
- OAuth 2.0认证:OAuth 2.0是一个开放标准,允许用户授权第三方应用程序访问其账户信息,而无需共享密码。Spring Security通过集成Spring OAuth项目来支持OAuth 2.0。
- OpenID Connect认证:OpenID Connect是基于OAuth 2.0的一个身份认证层,它允许应用程序通过安全的方式验证用户的身份,并获取关于用户的基本信息。
- SAML 2.0认证:安全断言标记语言(SAML)是一种基于XML的开放标准,用于在身份提供者和服务提供商之间交换身份验证和授权数据。Spring Security可以与SAML 2.0兼容的身份提供商集成。
- LDAP认证:轻量级目录访问协议(LDAP)是一种用于查询和修改目录服务的协议,如Active Directory。Spring Security可以通过LDAP服务器进行用户认证。
- JAAS认证:Java认证和授权服务(JAAS)是Java平台的一个标准API,用于进行用户认证和访问控制。Spring Security可以与JAAS集成,以支持基于Java的认证机制。
- 预身份验证:这是一种机制,其中用户的身份已经在应用程序的外部进行了验证,并且以某种方式传递给了应用程序。例如,在使用代理服务器或负载均衡器时,用户可能已经在这些组件上进行了身份验证。例如SSO. 需要继承于AbstractPreAuthenticatedProcessingFilter来获取相关认证信息进行认证。
- X.509证书认证:在这种认证方式中,用户的身份通过客户端证书进行验证,该证书由受信任的证书颁发机构(CA)签发。
- 自定义认证:Spring Security还允许开发人员实现自定义的认证机制,以适应特定的业务需求。
授权(Authorization)
授权在认证之后进行的,它决定了用户在系统内被允许执行哪些操作或访问哪些资源。这涉及到为用户分配权限的过程,权限决定了用户可以执行哪些任务或访问哪些数据。例如,在某些系统中,只有具有特定权限的用户才能访问特定资源,或者只有管理员才能执行某些操作。
- 常见的授权实现
在计算机系统中,授权是指控制用户或实体对系统资源的访问权限的过程。以下是授权的一些常见实现方式:
- 基于角色的访问控制(RBAC):这是最常见的授权方式之一。在RBAC中,权限被分配给角色,而角色则被分配给用户。例如,一个系统可能有“管理员”、“编辑”和“读者”等角色,每个角色有不同的权限集合。用户根据其被分配的角色来获得相应的权限。
- 基于属性的访问控制(ABAC):在这种方法中,访问决策是基于请求者、资源、环境条件和策略的属性来动态计算的。ABAC提供了更细粒度的控制,因为它可以根据多个属性来允许或拒绝访问。例如:根据用户所属机构来授予权限.
- 基于策略的访问控制(PBAC):PBAC是一种灵活的授权模型,它允许管理员定义策略来控制对资源的访问。这些策略可以基于时间、位置、用户行为或其他条件。
- 访问控制列表(ACL):ACL是一种授权机制,它明确指定哪些用户或用户组可以访问特定的资源,以及他们可以执行哪些操作。ACL通常与文件系统或数据库等系统资源关联。参照Linux的文件权限管理。
- 基于声明的访问控制(Claims-Based Access Control):在这种模型中,用户Principals的身份Identities被封装在一组声明Claims中,这些声明描述了用户的属性和权限。系统根据这些声明来做出授权决策.适用于大型系统.
- 能力为基础的访问控制(CapBAC):这是一种较新的授权模型,它强调赋予实体(如用户或进程)特定的“能力”,这些能力定义了它们可以执行的操作。CapBAC旨在减少权限管理的复杂性,并增强系统的安全性。
- 强制访问控制(MAC):强制访问控制是一种严格的访问控制策略,通常由系统强制执行,而不是由用户或进程控制。在MAC系统中,每个用户和对象都被分配一个安全级别,系统根据这些级别来允许或拒绝访问。
Spring Security支持的授权方式
在Spring Security中,授权通常通过RBAC和ACL来实现,但也可以结合其他模型来提供灵活的授权解决方案。Spring Security进行了抽象,主要由UserDetailsService
、UserDetails
、GrantedAuthority
三大接口来组织授权方式。
前面我们说过,在认证成功后,紧接着就是授权。但是实际上在SpringSecurity并不是这样实现的。在认证之前,SpringSecurity需要先找到当前想要登录的用户信息,然后才能进行凭证(密码)比较。而SpringSecurity则要求查找到的目标用户是完整的用户信息,也就是,把权限也查出来。所以当认证成功,就相当于自动完成授权了。
因此,不管我们要实现什么样的授权方式,只需要实现这三大接口进行扩展即可。PS:暂且不谈后续鉴权,如何处理,不是当前重点。
UserDetailService
Spring Security自己的实现
UserDetailService的实现,有两个:
InMemoryUserDetailsManager是基于内存的,用户信息保存在Map中。
JdbcUserDetailsManager则是基于数据库的,相关的表结构不妨看看其父类org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
的注释
使用JDBC查询从数据库中检索用户详细信息(用户名、密码、启用标志和权限)
包含两个表:“users”和“authorities”
用户表:users
列:username、password、enabled权限表:authorities
列:username、authority分组支持
通过将enableGroups属性设置为true,可以启用对基于组的权限的支持
使用这种方法,权限被分配给组,用户的权限基于他们所属的组来确定。
当使用组时,将使用“groups”,“group_members”和“group_authorities”表
PS: 下面的表结构是我补充的分组表: groups
列:id、group_name分组成员:group_members
列:username、group_id分组权限:group_authorities
列:group_id、authority
实际上,我们基本上不会直接使用原生的UserDetailService,因为我们的表不会如此简单。因此通常都是自己实现一个UserDetailService。
UserDetails
public interface UserDetails extends Serializable {
/**
* 返回用户被授予的权限。不能返回null。
* @return 被自然顺序排好的权限。不能为null
*/
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
Spring Security的实现:
org.springframework.security.core.userdetails.User
。如果我们只是基于GrantedAuthority扩展的权限实现,用它倒也够用了。
GrantedAuthority
public interface GrantedAuthority extends Serializable {
/**
* 如果是可以用String来表示的权限,那么足以{@link AccessDecisionManager}执行访问控制逻辑了。
* 只需要返回String就行。
*
* 如果不能用String来表示,那么应当返回null。而返回null的,将需要一个特定的AccessDecisionManager实现来支持。
* 所以非必要,应当避免返回null。
*
* @return 返回一个授予的权限(或者当无法用String表示的时候,返回null)
*/
String getAuthority();
}
实际上,AccessDecisionManager已经被AuthorizationManager所取代。对应地安全过滤器也存在取代关系。
但个人认为,新的AuthorizationFilter比原来的FilterSecurityInterceptor更加容易理解和掌握,不管是整体设计还是职责。
不过这是后面处理鉴权的时候要聊的,姑且跳过。
GrantedAuthority的简单实现:SimpleGrantedAuthority。
他只存了一个字符串。因此,我们可以基于他来实现一个RBAC的授权方式。不过,这个字符串里面填啥东西,全凭我们在加载UserDetails的时候往里面装啥东西了。你懂的,但可不要乱装哦。
好了,到此三大授权相关的组件就介绍完了。有了此三大组件,想要实现什么授权方式,不是全凭使用者如何定制了嘛。
小结
应该说,Spring Security支持哪些授权方式是不准确的,因为只要基于三大组件进行扩展,怎么都行。
且说Spring Security提供哪些原生支持的授权方式。
从实现上来说,Spring Security提供原生支持授权方式:
- 基于角色的访问控制(RBAC): 只是在授权相关的三大组件上,还有在鉴权的支持上:AuthoritiesAuthorizationManager.
- 基于属性的访问控制(ABAC):这种方式主要体现在鉴权上,因为用户信息本身就包含用户的相关属性。可以基于Spel实现。
- 访问控制列表(ACL):引入ACL依赖实现。
后记
其实在SpringSecurity的文档中, 个人认为Authorization更多代表的是鉴权, 而不是授权. 正如我们前面所说, 授权是在认证之后, 给用户分配权限的过程. 这一点与我们在讨论的授权方式这一概念上有很大区别. 甚至在我们聊到与授权相关的三大组件也没有出现Authorization这一单词的身影. 这也是我最初在概念上犯迷糊的地方.
下一节, 我们先聊聊权限配置. 只有搞懂了怎么配置权限, 我们才能知道怎么样进行鉴权. 至于为什么不从认证开始, 因为认证比较难, 先挑软骨头上手, 容易建立信心.