说明
本文的目的是如何基于 Spring Security 去扩展实现一个基本的用户权限模块, 内容会覆盖到 Spring Security 常用的配置.
文中涉及到的业务代码是不完善的, 甚至会存在逻辑上的漏洞, 业务部分请自行思考完善.
一、脱离框架实现认证和鉴权
Spring Security、Shiro 这种所谓的安全框架, 其核心作用就是 认证 和 鉴权, 当不使用这些框架时, 比较常规的实现方式如下:
- 定义一个登录接口用于验证用户身份 (认证)
- 定义一个过滤器验证用户的访问权限 (鉴权)
伪代码
下面贴出的是核心代码(有删减)
认证
// 登录接口
class LoginController {
@PostMapping("/login")
public Object login(HttpServletRequest request, HttpServletResponse response) {
// 获取登录用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 忽略非空校验
// 根据用户名查询数据库
User user = userDao.selectByUsername(username);
if(user == null) {
return "用户名不存在, 返回登录页";
}
// 验证密码
if(!password.equals(user.getPassword)) {
return "密码不正确, 返回登录页";
}
return "登录成功";
}
}
鉴权
// 权限过滤器
class PermissionFilter implements Filter {
public void doFilter(HttpServletRequest request, HttpServletResponse response) {
// 如果是登录请求, 直接放行
String url = request.getRequestUrl();
if("/login".equals(url)) {
return "放行";
}
// 从 session 中取出用户信息(包含用户名、权限等关键信息)
User user = session.get('user');
// 如果 user == null, 表示未登录
if user == null {
return "跳转至登录页";
}
// 判断用户是否有当前请求的访问权限
if(user.getPermissions().contains(url)) {
return "放行";
}
return "没有访问权限!"
}
}
以上便是一个基本的、固定思维的 认证和 **鉴权 **的思路, 它的缺点是 可移植性 和 扩展性不高. 改造难度类似于给一台苹果笔记本换内存条一样.
二、基于框架实现认证和鉴权
Spring Security 在扩展性上给我们带来的体验就像给一台普通台式机换内存条, 基本上就是买个内存条, 然后一插一拔就完事了.
它把第一种实现方式中的涉及到的可变因素, 都提供出了对应的配置项, 因此, 实现认证和鉴权就跟组装变形金刚一样.
下面来分析下, 如果要对上述的 伪代码进行配置抽取, 至少都需要配置什么:
-
登录接口
- 我们需要配置一个登录接口的
url
, 以及获取表单数据的参数名称, 例如:username
、password
- 如何从某种存储介质(
redis
、mysql
、memory
等)中取出用户信息(涉及到从哪里取, 怎么取).
- 我们需要配置一个登录接口的
-
权限过滤器
- 配置 url 的访问规则, 比如
/user
必须具有admin
角色的用户才能访问(涉及到如何访问规则, 以及如何匹配这些规则.)
- 配置 url 的访问规则, 比如
下面看一下 Spring Security 是如何配置上面提到的内容的:
自定义配置
首先需要了解的是 Spring Security 是有自己的默认配置的
- 默认提供了登录页, 通过
http://localhost:port/login
访问 - 默认接收登录表单的字段是
username
和password
- 默认提供了默认用户名
user
和 密码 (项目启动时, 会在控制台打印), 存放于内存中.
一、覆盖默认配置类
前面说到, Spring Security 有自己的默认配置, 如果要自定义这些配置, 我们就要通过某种方式, 去覆盖重写原有的配置, Spring Security 为我们提供了相应的入口 :
- 定义一个
WebSecurityConfig
配置类, 添加@EnableWebSecurity
注解 - 继承
WebSecurityConfigurerAdapter
类 - 重写对应的方法(下面会重点提到)
// 第一步: 添加注解
@EnableWebSecurity
// 第二步: 继承配置类
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// TODO 第三步: 重写方法
}
二、配置表单登录
上一步, 我们已经找到了重写配置的切入点, 下面就接着上一步, 去真正自定义配置
- 告诉 Spring Security 如何接收表单中的 **用户名 **和 密码, 也就是配置表单参数名称, 默认是
username
和