【膳逸】项目介绍

本文介绍了使用SaToken进行后端认证与权限控制的方法,包括Token原理、哈希算法应用、自定义返回类和分页工具,同时涵盖了前端的Ajax请求封装、前端权限验证以及小程序端的uView组件和分包策略。
摘要由CSDN通过智能技术生成

项目介绍

1. 后端

1.1. SaToken认证与授权

SaToken框架是国内新崛起的开源免费认证与授权框架,2021年荣获码云的GVP头衔。相关资料,大家可以看它的官方手册文档,里面有入门说明和AP接口个绍。在本课程项目中没有使用Shiro和SpringSecurity的原因非常简单:Shiro和SpringSecurity都没有内置JWT功能,所以我们需要额外配置JWT,过程非常繁琐。恰好SaToken内置了JWT功能,而且Token的缓存和过期管理都实现了,于是我就用SaToken框架。等你熟悉了SaToken框架之后,基本上再也不想碰Shiro和SpringSecurity了。

1.1.1. Token认证的原理

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

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

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

1.1.2. 判断是否登录

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

@PostMapping("/createFaceModel")
@SaCheckLogin // 验证用户是否已经登录,只有登录了才能调用此方法。是SaToken框架来执行的,从请求头中获取Token看是否有效。
public R createFaceModel(@RequestBody @Valid CreateFaceModelForm form){
	int userId StpUtil.getLoginIdAsInt();
	form.setUserId(userId);
	Map param BeanUtil.beanToMap(form);
	faceAuthService.createFaceModel(param);
	return R.ok();
}

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

1.1.3. 判断是否具有权限

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

@PostMapping("/searchByPage")
@SaCheckLogin // 用户必须登录
// 用户必须具备相关的权限,ROOT或(SaMode.OR)USER:SELECT
@SaCheckPermission(value = {"ROOT","USER:SELECT"}, mode = SaMode.OR)
public R searchByPage(@RequestBody @Valid SearchDoctorByPageForm form){
	Map param BeanUtil.beanToMap(form);
	int page = form.getPage();
	int length = form.getLength();
	int start = (page -1) * length;
	param.put("start", start);
	PageUtils pageUtils = misUserService.searchByPage(param);
	return R.ok().put("result", pageUtils);
}

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

@Component
public class StpInterfaceImpl implements StpInterface {
    @Resource
    private MisUserDao userDao;

    /**
     * 返回一个用户所拥有的权限集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginKey) {
        int userId = Integer.parseInt(loginId.toString());
        ArrayList<String> list = userDao.searchUserPermissions(userId);
        return list;
    }


    /**
     * 返回一个用户所拥有的角色标识集合(不建议使用,所以直接返回空)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginKey) {
        return null;
    }
}

1.2. 哈希算法对密码加密

1.2.1. 哈希加密

1.2.2. 哈希字典反破解

1.2.3. 多次哈希加密

1.2.4. 原始数据加盐混淆

1.3. 自定义返回类R

1.3.1. 默认调用成功
return R.ok();

返回格式:
{
    "code": 200,
    "msg": "success"
}

1.3.2. 自定义msg为String

当然也可以自定义返回的msg消息。

return R.ok("操作成功");

返回格式:
{
    "code": 200,
    "msg": "操作成功"
}

1.3.3. 自定义msg为Map
HashMap map = userService.searchById(form.getId());
return R.ok(map);

返回格式:
{
  "msg": "success",
  "birthday": "1901-01-01",
  "code": 200,
  "address": "山东济南",
  "goal": "增肌",
  "nickName": "LxPl2net",
  "sex": "男",
  "weight": "80.2",
  "diseases": [
    "糖尿病"
  ],
  "dislikes": [],
  "avatar": "https://thirdwx.qlogo.cn/mmopen/vi_32/5icHNmy3LXpUEY3JHKrm4P0twqobiaYuemvXr4s6aK2rJKQ7RkVOaP24nRSkEfwErCIzkSUyvibFicJ37IA5Yppumg/132",
  "uuid": "c948897c4ace4268a22a1922ea7bba18",
  "phone": "15523x81028",
  "name": "李x",
  "id": 3,
  "email": "25282x331@qq.com",
  "height": "1.94",
  "bmi": "21.31",
  "likes": [
    "辣椒"
  ],
  "status": 1
}

1.3.4. 使用put()绑定数据,可链式调用
PageUtils pageUtils = userService.searchByPage(param);
return R.ok().put("result", pageUtils); // 后面如果还有想要绑定的数据,可以继续.put()

返回格式:
{
  "msg": "success",
  "result": {
    "totalCount": 2,
    "pageSize": 10,
    "totalPage": 1,
    "pageIndex": 1,
    "list": [
      {
        "phone": "15523x81028",
        "nickName": "LxPl2net",
        "name": "李x",
        "id": 3,
        "email": "25282x331@qq.com",
        "status": 1
      },
      {
        "phone": "15925823218",
        "createTime": "2024-04-23",
        "nickName": "k2mi",
        "name": "李xx",
        "id": 4,
        "email": "2291262972@qq.com",
        "status": 1
      }
    ]
  },
  "code": 200
}

1.3.5. 返回错误

当然,也有对应的返回错误信息的方法。

1.4. 分页工具类

可以将查询出来的数据与分页的相关参数进行封装,返回给前端。

使用方法:

2. MIS端前端

2.1. 前端项目架构

├─src                                   # 
│  │                                    # 
│  ├─node_modules                       # npm下载下来的依赖库
|  |                                    #
│  ├─assets                             # 放图片、scss文件等静态资源
│  │  │  404.png                        # 
│  │  │                                 # 
│  │  ├─login                           # 登录界面用到的静态资源
│  │  │      bg.svg                     # 登录界面背景
│  │  │      sy-logo.png                # 登录界面Logo
│  │  │                                 # 
│  │  └─scss                            # 
│  │          base.scss                 # 
│  │          index.scss                # 
│  │          normalize.scss            # 
│  │          variables.scss            # 
│  │                                    # 
│  ├─components                         # 自定义组件
│  │      SvgIcon.vue                   # 自定义图标组件
│  │                                    # 
│  ├─icons                              # 图标
│  │  └─svg                             # 
│  │          icon-admin.svg            # 
│  │          icon-assurance_fill.svg   # 
│  │          icon-bianji.svg           # 
│  │          ...                       # 
│  │                                    # 
│  ├─router                             # 
│  │      index.js                      # 路由配置
│  │                                    # 
│  ├─utils                              # 
│  │      validate.js                   # 前端验证:在提交ajax请求之前在前端进行验证
│  │                                    # 
│  ├─views                              # 各路由页面
│  │       404.vue                      # 
│  │       app_user-add-or-update.vue   # 
│  │       app_user.less                # 
│  │       app_user.vue                 # 
│  │       home.less                    # 
│  │       home.vue                     # 
│  │       login.less                   # 
│  │       login.vue                    # 登录界面
│  │       main.vue                     # 
│  │       update-password.vue          # 
│  │       user.less                    # 
│  │       user.vue                     # 
│  │                                    # 
│  │  App.vue                           # App.vue页面:路由切换页面时
|  |                                    # 其实就是让App.vue加载不同页面进来
|  |                                    #
│  │  main.js                           # 前端项目的入口文件:封装全局Ajax公共函数,
│                                       # 封装用于判断用户是否具有某些权限的公共函数
│                                       # 
├─static                                # 
│                                       # 
│  .gitignore                           # 
│  favicon.png                          # 网站logo:在这里修改网站logo图标
│  index.html                           # 基座文件:在这里修改网站名
│  package-lock.json                    # 
│  package.json                         # 前端项目使用的依赖库
│  vite.config.js                       # vite脚手架配置文件:在这里修改端口号、跨域、
                                        # 自启浏览器、引入elementplus和svg组件库等

2.2. 自定义封装Ajax请求

我们使用的是JQuery的Ajax技术,并不是网上推荐的Axios技术。到底是出于什么原因放弃Axios了呢?原因和简单:JQuery支持同步和异步的Ajax请求,而Axios只支持异步请求。说到这里,大家可能会产生疑惑:Ajax本来不就应该是异步执行的么?
其实还是前端代码写的少,而且没有做过复杂业务的项目,所以就以为Ajax本应该是异步的。在特定的场景下,我们只能用同步的Ajax请求,而决不能用异步请求。先说一个现实生活中的例子,比如说你在打游戏的时候,别人直接过来拔掉电脑电源,游戏都没来得及存档就关机了。明白这个道理,下面的例子就好理解了。比如说我们的项目使用了ElementPlus组件库,其中有很多组件有回调函数。如果我们如果在回调函数中发起异步Ajax请求,ElementPlus的回调函数不会等着异步请求执行完,直接就结束了,并且销毁了调用栈,从而导致异步Ajax强制被退出。这就像直接拔掉电脑电源,不管你电脑正在运行什么程序都直接被关闭。

2.3. 前端权限验证

用户登陆成功之后,后端Java项目会在HTTP响应里放入Token令牌和用户拥有的权限列表。前端项目需要把Token和权限列表保存到浏览器的Storage里面。前端VUE页面在渲染的时候,需要根据权限判定哪些控件可以显示,这就逼着我们把权限验证封装成公共函数。

app.config.globalProperties.isAuth function(permission) {
	let permissions = localStorage.getItem("permissions");
	let flag = false
	for (let one of permission) {
		if (permissions.includes(one)) {
			flag true
			break;
     	}
    }
	return flag;
}

Vue页面的组件需要验证权限的时候,可以写成下面的样子:

<template>
	<div v-if="isAuth(['ROOT', 'USER:SELECT'])">
		...
	</div>
</template>

3. 小程序端前端

3.1. uView组件库

官网

https://www.uviewui.com/

uView自带icons图标

https://www.uviewui.com/components/icon.html

3.2. 小程序分包

微信规定小程序打包后的体积不能超过2M。如果小程序页面较代码打包体积超过了2M,我们可以采用分包的形式,把主包的页面拆分到其他分包中,只要每个分包的体积不超过2M,而且小程序打包总体积不超过64M就可以。本课程的小程序项目就采用了分包加载的方式,主包是pages目录,分包是user这些目录。

3.3. 封装Ajax请求

与前端Vue项目类似,小程序项目也定义了全局的Ajax封装函数,在main.js文件里面有该函数的声明。

3.4. 小程序项目架构

├─.hbuilderx                    # hbuilderx自动生成的目录,不用管,可以删除
│      launch.json              # 
│                               # 
├─node_modules                  # npm下载下来的依赖库
|                               #
├─pages                         # 小程序的页面,主包
│  ├─activity                   # 
│  │      activity.less         # 
│  │      activity.vue          # 
│  │                            # 
│  ├─camera                     # 
│  │      camera.less           # 
│  │      camera.vue            # 
│  │                            # 
│  ├─chat                       # 
│  │      chat.less             # 
│  │      chat.vue              # 
│  │                            # 
│  ├─check_in                   # 
│  │      check_in.less         # 
│  │      check_in.vue          # 
│  │                            # 
│  └─mine                       # 
│          mine.less            # 
│          mine.vue             # 
│                               # 
├─static                        # 静态资源,放了一些图标,但主要还是存在minio中
│  ├─picture                    # 
│  │      u274.png              # 
│  │                            # 
│  └─tab_bar                    # 
│          dkw_拍摄.png          # 
│          dkw_拍摄3.png         # 
│          个人中心.png          # 
│          个人中心1.png         # 
│          打卡2.png             # 
│          打卡4.png             # 
│          活动2.png             # 
│          活动5.png             # 
│          聊天.png              # 
│          聊天3.png             # 
│                               # 
├─user                          # 分包
│  └─fill_user_info             # 
│          fill_user_info.less  # 
│          fill_user_info.vue   # 
│                               # 
├─unpackage                     # 打包的内容
│  └─dist                       # 
|                               #
├─uview-ui                      # uView组件库
│  ├─components                 # 注意:这里面有个包叫u-icon,里面有个文件叫做icons.js,
|  | ...                        # 里面是uView自带的icons
|                               # 
│  .gitignore                   # 
│  App.vue                      # 
│  index.html                   # 基座文件
│  main.js                      # 入口文件,封装了Ajax
│  manifest.json                # 小程序相关配置
│  package-lock.json            # 
│  package.json                 # 小程序依赖的第三方库
│  pages.json                   # 路由配置
│  style.less                   # 自定义全局样式
│  uni.scss                     # 小程序自带的样式文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lhplanet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值