写点什么

“程”风破浪的开发者|satoken 实现优雅鉴权

作者:codingyt
  • 2022-10-27
    山东
  • 本文字数:6631 字

    阅读完需:约 22 分钟

“程”风破浪的开发者|satoken实现优雅鉴权

https://sa-token.dev33.cn/doc/index-backup.html#/satoken 官方网址(很好用的鉴权)

依赖引入

<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ --><dependency>    <groupId>cn.dev33</groupId>    <artifactId>sa-token-spring-boot-starter</artifactId>    <version>1.30.0</version></dependency>
复制代码

yml 文件配置(中文版 进行理解的)

server:    # 端口    port: 8081
# Sa-Token配置sa-token: # token 名称 (同时也是cookie名称) token-name: satoken # token 有效期,单位s 默认30天, -1代表永不过期 timeout: 2592000 # token 临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 activity-timeout: -1 # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) is-concurrent: true # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) is-share: false # token风格 token-style: uuid # 是否输出操作日志 is-log: false
复制代码

yml 文件配置(英文版本 直接放到项目里面的)

# Sa-Token settingsa-token:
token-name: satoken
timeout: 2592000
activity-timeout: -1
is-concurrent: true
is-share: false
token-style: uuid
is-log: false
复制代码

启动类加上日志打印

 System.out.println("启动成功:Sa-Token配置如下:" + SaManager.getConfig());
复制代码


常用的几个内置函数

StpUtil.login(10001); #登陆成功之后使用 通常我们登陆成功之后放一个用户唯一的标识进去即可StpUtil.isLogin(); 检测用户是否登陆SaResult.data(StpUtil.getTokenInfo()); 获取用户的token信息的StpUtil.logout(); 注销我们的账户的 可以随时退出
复制代码

权限获取类(用于获取我们的用户的标识)

package com.itheima.utils;
import cn.dev33.satoken.stp.StpInterface;import cn.dev33.satoken.stp.StpUtil;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.springframework.stereotype.Component;
import java.util.ArrayList;import java.util.List;
/** * 自定义权限验证接口扩展 */@Component // 保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展public class StpInterfaceImpl implements StpInterface {
/** * 返回一个账号所拥有的权限码集合 */ @Override public List<String> getPermissionList(Object loginId, String loginType) { // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限 //loginId:账号id,即你在调用 StpUtil.login(id) 时写入的标识值。 //loginType:账号体系标识,此处可以暂时忽略,在 [ 多账户认证 ] 章节下会对这个概念做详细的解释。 StpUtil.getLoginId();//我们可以根据这个来进行我们的角色的信息认证 //通过调用我们的 QueryWrapper<Object> wrapper = new QueryWrapper<>(); //查询到我们的用户信息 List<String> list = new ArrayList<String>(); list.add("user-add"); list.add("user-delete"); list.add("user-update"); list.add("user-get"); list.add("article-get"); return list; }
/** * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验) */ @Override public List<String> getRoleList(Object loginId, String loginType) { // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色 List<String> list = new ArrayList<String>(); list.add("admin"); list.add("super-admin"); return list; }
}
复制代码

开启注解鉴权

package com.itheima.config;
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//配置我们的注解拦截器 这里我们的springboot的版本值如果太高的话 我们就不能直接这样做 具体情况具体分析@EnableWebMvc//高版本就加 如果不是就可以不用加@Configurationpublic class SaTokenConfigure implements WebMvcConfigurer { // 注册Sa-Token的注解拦截器,打开注解式鉴权功能 @Override public void addInterceptors(InterceptorRegistry registry) { // 注册注解拦截器,并排除不需要注解鉴权的接口地址 (与登录拦截器无关) registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**"); }}
复制代码

注意:如果在高版本 SpringBoot (≥2.6.x) 下注册拦截器生效,则需要额外添加 @EnableWebMvc 注解才可以使用。

注解鉴权实际案列演示


加密模块

自定义加密的密文(目前在使用的)


package com.sdjz.xgk.utils;
import cn.dev33.satoken.secure.SaSecureUtil;
/** * @author yt1105 * @version 1.0 */public class secre { public static final String key = "jkadjkajksdjkad";//自己定义属于自己秘钥进行加密处理
//生成秘钥(存到数据库里面的) public static String generatePassword(String password) { String ciphertext = SaSecureUtil.aesEncrypt(key, password); return ciphertext; }

//这里传递的是我们的加密后的数据(这个是加密之后的数据 才能调用这个接口才行) public static String getDecrypt(String password) { String text2 = SaSecureUtil.aesDecrypt(key, password); return text2; }}
复制代码


// 定义秘钥和明文String key = "123456";String text = "Sa-Token 一个轻量级java权限认证框架";
// 加密 String ciphertext = SaSecureUtil.aesEncrypt(key, text);System.out.println("AES加密后:" + ciphertext);
// 解密 String text2 = SaSecureUtil.aesDecrypt(key, ciphertext);System.out.println("AES解密后:" + text2);
复制代码

非对称加密

RSA 加密(这个使用的话很可能会导致我们的代码出现一定的问题 字符长度过长可能会导致没法存到数据库中去)


// 定义私钥和公钥 String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAO+wmt01pwm9lHMdq7A8gkEigk0XKMfjv+4IjAFhWCSiTeP7dtlnceFJbkWxvbc7Qo3fCOpwmfcskwUc3VSgyiJkNJDs9ivPbvlt8IU2bZ+PBDxYxSCJFrgouVOpAr8ar/b6gNuYTi1vt3FkGtSjACFb002/68RKUTye8/tdcVilAgMBAAECgYA1COmrSqTUJeuD8Su9ChZ0HROhxR8T45PjMmbwIz7ilDsR1+E7R4VOKPZKW4Kz2VvnklMhtJqMs4MwXWunvxAaUFzQTTg2Fu/WU8Y9ha14OaWZABfChMZlpkmpJW9arKmI22ZuxCEsFGxghTiJQ3tK8npj5IZq5vk+6mFHQ6aJAQJBAPghz91Dpuj+0bOUfOUmzi22obWCBncAD/0CqCLnJlpfOoa9bOcXSusGuSPuKy5KiGyblHMgKI6bq7gcM2DWrGUCQQD3SkOcmia2s/6i7DUEzMKaB0bkkX4Ela/xrfV+A3GzTPv9bIBamu0VIHznuiZbeNeyw7sVo4/GTItq/zn2QJdBAkEA8xHsVoyXTVeShaDIWJKTFyT5dJ1TR++/udKIcuiNIap34tZdgGPI+EM1yoTduBM7YWlnGwA9urW0mj7F9e9WIQJAFjxqSfmeg40512KP/ed/lCQVXtYqU7U2BfBTg8pBfhLtEcOg4wTNTroGITwe2NjL5HovJ2n2sqkNXEio6Ji0QQJAFLW1Kt80qypMqot+mHhS+0KfdOpaKeMWMSR4Ij5VfE63WzETEeWAMQESxzhavN1WOTb3/p6icgcVbgPQBaWhGg==";String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvsJrdNacJvZRzHauwPIJBIoJNFyjH47/uCIwBYVgkok3j+3bZZ3HhSW5Fsb23O0KN3wjqcJn3LJMFHN1UoMoiZDSQ7PYrz275bfCFNm2fjwQ8WMUgiRa4KLlTqQK/Gq/2+oDbmE4tb7dxZBrUowAhW9NNv+vESlE8nvP7XXFYpQIDAQAB";
// 文本String text = "123123123";
// 使用公钥加密String ciphertext = SaSecureUtil.rsaEncryptByPublic(publicKey, text);System.out.println("公钥加密后:" + ciphertext);
// 使用私钥解密String text2 = SaSecureUtil.rsaDecryptByPrivate(privateKey, ciphertext);System.out.println("私钥解密后:" + text2);
复制代码


// 生成一对公钥和私钥,其中Map对象 (private=私钥, public=公钥)System.out.println(SaSecureUtil.rsaGenerateKeyPair());
复制代码

登陆模块

package com.itheima.controller;
import cn.dev33.satoken.stp.StpUtil;import cn.dev33.satoken.util.SaResult;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
/** * @author yt1105 * @version 1.0 *///注解可以加上之后进行全局的拦截处理@RestController@RequestMapping("/user/")public class loginController { @RequestMapping("doLogin") public SaResult doLogin(String username, String password) { // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对 // todo 前台传递给我们的 账号密码 我们获取一个id 将我们的数据库的数据进行比对 // 然后将我们的用户的id进行比对 if ("zhang".equals(username) && "123456".equals(password)) { StpUtil.login(10001); return SaResult.ok("登录成功"); } return SaResult.error("登录失败"); }
// 查询登录状态,浏览器访问: http://localhost:8081/user/isLogin @RequestMapping("isLogin") public String isLogin() { return "当前会话是否登录:" + StpUtil.isLogin(); }

//检测我们的用户的token信息 @RequestMapping("tokenInfo") public SaResult tokenInfo() { return SaResult.data(StpUtil.getTokenInfo()); }

// todo 到时候 我们的前提只要一调用这个接口的话 我们就直接注销这个用户的信息 // 测试注销 ---- http://localhost:8081/acc/logout @RequestMapping("logout") public SaResult logout() { StpUtil.logout(); return SaResult.ok(); }}
复制代码

获取权限信息

package com.sdjz.xgk.security.roles;
import cn.dev33.satoken.stp.StpInterface;import cn.dev33.satoken.stp.StpUtil;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.sdjz.xgk.entity.SysUser;import com.sdjz.xgk.mapper.SysUserMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;
import java.util.ArrayList;import java.util.List;
/** * 自定义权限验证接口扩展 */@Component // 保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展public class StpInterfaceImpl implements StpInterface { @Autowired SysUserMapper sysUserMapper;
@Override public List<String> getPermissionList(Object loginId, String loginType) { List<String> list = new ArrayList<String>(); String loginId1 = (String) StpUtil.getLoginId(); //通过调用我们的 QueryWrapper<SysUser> wrapper = new QueryWrapper<>(); wrapper.like("phone", loginId1); SysUser sysUser = sysUserMapper.selectOne(wrapper); Integer role = sysUser.getRole(); //查询到我们的用户信息 if (role.equals(0)) {//书写自己逻辑 //普通用户 list.add("user-add"); list.add("user-delete"); list.add("user-update"); } if (role.equals(1)) { //普通用户 list.add("user-add"); list.add("user-delete"); list.add("user-update"); } if (role.equals(2)) { //普通用户 list.add("user-add"); list.add("user-delete"); list.add("user-update"); } return list; }
/** * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验) */ @Override public List<String> getRoleList(Object loginId, String loginType) { List<String> list = new ArrayList<String>(); String loginId1 = (String) StpUtil.getLoginId(); //通过调用我们的 QueryWrapper<SysUser> wrapper = new QueryWrapper<>(); wrapper.like("phone", loginId1); SysUser sysUser = sysUserMapper.selectOne(wrapper); Integer role = sysUser.getRole(); //查询到我们的用户信息 if (role.equals(0)) { //普通用户 list.add("admin");
} if (role.equals(1)) { //专家 list.add("admin"); } if (role.equals(2)) { //admin list.add("admin"); list.add("user"); } return list; }
}
复制代码

退出登陆模块

// 测试注销  ---- http://localhost:8081/acc/logout    @RequestMapping("logout")    public SaResult logout() {        StpUtil.logout();        return SaResult.ok();    }
复制代码

前后端分离(微信小程序)

1、后端将 token 返回到前端

  1. 首先调用 StpUtil.login(id) 进行登录。

  2. 调用 StpUtil.getTokenInfo() 返回当前会话的 token 详细参数。

  3. 此方法返回一个对象,其有两个关键属性:tokenName 和 tokenValue(token 的名称和 token 的值)。

  4. 将此对象传递到前台,让前端人员将这两个值保存到本地。


代码示例:


// 登录接口@RequestMapping("doLogin")public SaResult doLogin() {    // 第1步,先登录上     StpUtil.login(10001);    // 第2步,获取 Token  相关参数     SaTokenInfo tokenInfo = StpUtil.getTokenInfo();    // 第3步,返回给前端     return SaResult.data(tokenInfo);}
复制代码

2、前端将 token 提交到后端

  1. 无论是 app 还是小程序,其传递方式都大同小异。

  2. 那就是,将 token 塞到请求 header 里 ,格式为:{tokenName: tokenValue}。

  3. 以经典跨端框架 uni-app 为例:


方式 1 简单粗暴(官网获取)


// 1、首先在登录时,将 tokenValue 存储在本地,例如:uni.setStorageSync('tokenValue', tokenValue);
// 2、在发起ajax请求的地方,获取这个值,并塞到header里 uni.request({ url: 'https://www.example.com/request', // 仅为示例,并非真实接口地址。 header: { "content-type": "application/x-www-form-urlencoded", "satoken": uni.getStorageSync('tokenValue') // 关键代码, 注意参数名字是 satoken }, success: (res) => { console.log(res.data); }});
复制代码


剪贴板错误


**方式2更加灵活(官网获取)**```java// 1、首先在登录时,将tokenName和tokenValue一起存储在本地,例如:uni.setStorageSync('tokenName', tokenName); uni.setStorageSync('tokenValue', tokenValue); 
// 2、在发起ajax的地方,获取这两个值, 并组织到head里 var tokenName = uni.getStorageSync('tokenName'); // 从本地缓存读取tokenName值var tokenValue = uni.getStorageSync('tokenValue'); // 从本地缓存读取tokenValue值var header = { "content-type": "application/x-www-form-urlencoded"};if (tokenName != undefined && tokenName != '') { header[tokenName] = tokenValue;}
// 3、后续在发起请求时将 header 对象塞到请求头部 uni.request({ url: 'https://www.example.com/request', // 仅为示例,并非真实接口地址。 header: header, success: (res) => { console.log(res.data); }});
```
1. 只要按照如此方法将token值传递到后端,Sa-Token 就能像传统PC端一样自动读取到 token 值,进行鉴权。### 前后端分离(web端 以vue为例)
复制代码


发布于: 刚刚阅读数: 3
用户头像

codingyt

关注

还未添加个人签名 2022-10-25 加入

还未添加个人简介

评论

发布
暂无评论
“程”风破浪的开发者|satoken实现优雅鉴权_学习方法_codingyt_InfoQ写作社区