基本概念
SpringSecurity是Spring下的一个安全框架,与shiro 类似,一般用于用户认证(Authentication)和用户授权(Authorization)两个部分,常与与SpringBoot相整合。
认证
认证的主要目的就是“证明你是谁”,常见的认证方式:
- 账号密码登录
- 生物认证,例如指纹,人脸,瞳孔
- 动态口令,例如例如手机验证码
授权
再认证之后,系统会赋予你一定的权利,这就是授权,即“你可以做什么”
授权模型
为了对权限的管理,我们通常把”权限相关的内容“存放在数据库中,一个用户可以拥有多个角色,而每个角色可以包含多个权限。
授权的方法(RBAC)
- 基于角色的授权方法:当用户拥有某个角色时,且这个角色拥有某项操作权限,那么这个用户就拥有这个操作权限
- 基于资源的授权方式:当用户拥有某个角色时拥有某项操作权限,那么这个用户就可以对这个资源进行相关操作
通常建议在程序层面采用基于资源的授权,而数据库层面采用 基于角色的授权,原因如下:
首先我们的授权系统需要保证是优秀的,那么什么是优秀的系统:
- 便于对用户权限更改
- 用户与权限之间,耦合度小
- 代码精简
首先,先考虑程序层面,如果我们在程序层面用的是基于角色的授权方式,一旦我们需要对某个角色的权限做出修改,我们就不得不修改代码。如:
1 | if(达芬奇.hasRole("画家")){ |
当画室不再对外开放,而达芬奇的画家身份又是不变的,我们就不得不修改代码,但如果我们在程序层面使用对资源的授权方式,就不需要修改代码。
1 | if(达芬奇.hasAuth("使用画室")){ |
当我们不希望达芬奇使用画室,我们就可以在数据库中,删除达芬奇”使用画室“的权限。也就是说,基于资源的授权方式更灵活。
接下来,考虑数据库层面,如果在数据库中,我们使用的是基于角色的授权方式,即只有三张表,用户表,权限表,用户权限关系表。此时我们对用户的授权就拥有了很大的麻烦,比如:
当一个用户注册,加入到了这个系统中,你需要赋予他一些初始的权限,由于系统可能很复杂,即使是初始权限,也会很多。在数据库中,我们需要多次调用insert操作,为一个用户授权,这样对于代码来讲,是很庞大的工作量,而且多次I/O操作也会导致性能很低。
但如果我们使用基于角色的方式,问题就会得到解决,在用户注册后,我们只需要给他赋予一个特定的角色,在数据库中,这个角色拥有着一些初始权限。这样很方便我们进行管理。也就是说,基于角色的权限管理更易管理。
总结
基于资源的授权方式更灵活。基于角色的权限管理更易管理。所以我们可以各取所长,放到合适的地方,在程序层面采用基于资源的授权,而数据库层面采用 基于角色的授权
Spring security原理
Spring security对于认证可授权的管理是基于session的,通过过滤器链,主要和两个类打交道,一个是认证类,一个是授权类。
- 首先他他会判断你有没有进行过认证,如果没有则会返回403或跳转到相应页面(这里可以自定义)
- 如果已经认证过,他会根据你带来的SesionID去Tomcat中查询此用户的权限,加入到当前会话中
- 当你访问特定API接口,他会判断当前会话是否拥有某个权限,如果有则会返回结果,没有返回403
认证流程
- 首先用户提交账号密码
- 经过一系列转化,最终将账号密码发送给UserDetailsService
- UserDetailsService会根据发送来的用户名,从数据库(或本地内存)中获取相应的用户信息封装为UserDetails包含三部分
- 用户名
- 加密后的密码
- 权限
- AuthenticationProvider会将用户发送来的信息,与UserDetails的信息相比对,如果成功,则生成SessionID,并将相应SessionID对应的权限保存到内存中
- 在认证时,可以设置密码加密器(在数据库中,密码不会以明文形式存储)
- 如果是在前后端分离的情况下,可以设置取消使用session
授权流程
- 系统会根据发送过来的sessionid,来获取此用户的权限
- 然后查询访问此接口,需要什么权限
- 接下来,会判断用户权限,是否包含访问此接口需要的权限,如果是则返回结果,否则返回403
总结
Spring security为认证和授权进行了大部分的工作,但存在一个问题,这些都是基于Session的,也就是说不适合于前后端分离项目。当然也有解决方案,使用Spring security与JWT整合。