目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

SaToken

JWT

    JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

    下列场景中使用 JSON Web Token 是很有用的:

  • Authorization (授权) : 这是使用 JWT 的最常见场景。一旦用户登录,后续每个请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的 JWT 的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens 无疑是一种很好的方式。因为 JWT 可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

image.png

    传统的单体 JavaWeb 项目通常采用 HttpSession 保存登陆成功的凭证,但是 HttpSession 需要浏览器的 Cookie 机制配合。也就是说 Web 项目的客户端只能是浏览器,不可以是 APP、机顶盒、智能家电等。
图片描述

    为了让 Java 项目兼容更多的客户端设备,所以我们要放弃在客户端用 Cookie 保存 SessionId 的做法。我们在服务端生成 Token 字符串,然后交给客户端保存。APP 也好、浏览器也好、机顶盒也好,都能保存字符串。每次发送 HTTP 请求的时候,在请求头上附带 Token 字符串,SaToken 框架就能解析出该 Token 是否合法,如果没有问题,就允许客户端请求后端的 Web 方法。

图片描述

    即便有人破解了 SaToken 生成令牌的算法,我们也不用担心,因为我们开启了 Redis 保存 Token 副本的功能。如果 SaToken 检测到请求头的 Token 在 Redis 中没有缓存副本,那么这个 Token 肯定是伪造的,所以就拒绝请求。另外 Redis 中的 Token 有过期时间,客户端提交的 Token 是过期的,SaToken 也能判断出来,然后拒绝请求。

SaToken

    SaToken 框架是国内新崛起的开源免费认证与授
权框架,2021 年荣获码云的 GVP 头衔。
    Shiro 和 SpringSecurity 都没有内置 JWT 功能,所以需要额外配置 JWT,过程非常繁琐。恰好 SaToken 内置了 JWT 功能,而且 Token 的缓存和过期管理都实现了。

判断是否登陆

    想要判断用户是否已经在客户端登陆,我们在 Web 方法上添加 @SaCheckLogin 注解即可。SaToken 只要检测到 Token 是有效的,就会允许 HTTP 请求调用该 Web 方法。

1@PostMapping("/searchById")
2    @SaCheckLogin
3    @SaCheckPermission(value = {"ROOT", "DOCTOR:SELECT"}, mode = SaMode.OR)
4    public R searchById(@RequestBody @Valid SearchDoctorByIdForm form) {
5        HashMap map = doctorService.searchById(form.getId());
6        return R.ok(map);
7    }

    无论哪位用户登陆患者端小程序,拥有的权限都是相同的。不存在不同身份的人去登陆患者端小程序,能登陆患者端小程序的人,都是相同身份的人。所以后端 Java 项目不需要判断他们是否具备不同的权限,这些患者具有相同的权限,因此我们只需要判断他们是否登陆即可。

判断是否具有权限

    在 MIS 端登陆的用户可能是超级管理员,也可能是普通医护人员,他们拥有的权限是不同的。所以我们要在 hospital-api 项目中验证用户是否登陆,而且还要验证他们是否具备相关的权限才能调用某个 Web 方法。这需要用到 @SaCheckPermission 注解了。

 1@PostMapping("/searchByPage")
 2    @SaCheckLogin
 3    @SaCheckPermission(value = {"ROOT", "DOCTOR:SELECT"}, mode = SaMode.OR)
 4    public R searchByPage(@RequestBody @Valid SearchDoctorByPageForm form) {
 5        Map param = BeanUtil.beanToMap(form);
 6        int page = form.getPage();
 7        int length = form.getLength();
 8        int start = (page - 1) * length;
 9        param.put("start", start);
10        PageUtils pageUtils = doctorService.searchByPage(param);
11        return R.ok().put("result", pageUtils);
12    }

    每当 SaToken 框架执行到 @SaCheckPermission 注解的时候,就会调用 StpInterfaceImpl 类。从里面的 getPermissionList() 函数中获取用户具备的权限,然后跟注解要求的权限比对。如果用户拥有相关权限就可以调用 Web 方法,否则就拒绝请求抛出异常。

 1@Component
 2public class StpInterfaceImpl implements StpInterface {
 3    @Resource
 4    private MisUserDao userDao;
 5
 6    /**
 7     * 返回一个用户所拥有的权限集合
 8     */
 9    @Override
10    public List<String> getPermissionList(Object loginId, String loginKey) {
11        int userId = Integer.parseInt(loginId.toString());
12        ArrayList<String> list = userDao.searchUserPermissions(userId);
13        return list;
14    }
15    
16    /**
17     * 返回一个用户所拥有的角色标识集合
18     */
19    @Override
20    public List<String> getRoleList(Object loginId, String loginKey) {
21        return null;
22    }
23}

作者:Soulboy