1. 接口开发
1.1 JWT令牌 🍐
前言


接下来,我们来学习Token
JWT全称:JSON Web Token (官网:https://jwt.io/)
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
简洁:是指jwt就是一个简单的字符串。可以在请求参数或者是请求头当中直接传递。
自包含:指的是jwt令牌,看似是一个随机的字符串,但是我们是可以根据自身的需求在jwt令牌中存储自定义的数据内容。如:可以直接在jwt令牌中存储用户的相关信息。

简单来讲,jwt就是将原始的json数据格式进行了安全的封装,这样就可以直接基于jwt在通信双方安全的进行信息传输了。JWT的组成: (JWT令牌由三个部分组成,三个部分之间使用英文的点来分割)
第一部分:Header(头), 记录令牌类型、签名算法等。 例如:
{"alg":"HS256","type":"JWT"}
第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:
{"id":"1","username":"Tom"}
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
签名的目的就是为了防jwt令牌被篡改,而正是因为jwt令牌最后一个部分数字签名的存在,所以整个jwt 令牌是非常安全可靠的。一旦jwt令牌当中任何一个部分、任何一个字符被篡改了,整个令牌在校验的时候都会失败,所以它是非常安全可靠的。

JWT是如何将原始的JSON格式数据,转变为字符串的呢?
其实在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码
Base64🚀:是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码,那也就意味着也能解码。所使用的64个字符分别是A到Z、a到z、 0- 9,一个加号,一个斜杠,加起来就是64个字符。任何数据经过base64编码之后,最终就会通过这64个字符来表示。当然还有一个符号,那就是等号。等号它是一个补位的符号
需要注意的是Base64是编码方式,而不是加密方式。
使用JWT令牌时需要注意:
- JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
- 如果JWT令牌解析校验时报错,则说明 JWT令牌被篡改 或 失效了,令牌非法。

JWT令牌最典型的应用场景就是登录认证 👇

代码操作
1. 生成JWT令牌
简单介绍了JWT令牌以及JWT令牌的组成之后,接下来我们就来学习基于Java代码如何。

直接使用代码中的工具类进行验证:

运行测试方法:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwidXNlcklkIjoxLCJleHAiOjE3NTMzNTQyMjR9.SDxuyOw01ttNC8TBWbjALfmpXqDa9ifS3JVPxkcJb74
输出的结果就是生成的JWT令牌,,通过英文的点分割对三个部分进行分割,我们可以将生成的令牌复制一下,然后打开Jwt解析网站(https://www.json.cn/jwt),将生成的令牌直接放在Encoded位置,此时就会自动的将令牌解析出来。

第一部分解析出来,看到JSON格式的原始数据,所使用的签名算法为HS256。
第二个部分是我们自定义的数据,之前我们自定义的数据就是id,还有一个exp代表的是我们所设置的过期时间。
由于前两个部分是base64编码,所以是可以直接解码出来。但最后一个部分并不是base64编码,是经过签名算法计算出来的,所以最后一个部分是不会解析的。
你可删除一点最后一段字母,观察解析结果的变化

2. 校验JWT令牌(解析生成的令牌)
实现了JWT令牌的生成,下面我们接着使用Java代码来校验JWT令牌(解析生成的令牌):
@Test
public void parseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("itheima")//指定签名密钥(必须保证和生成令牌时使用相同的签名密钥)
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk")
.getBody();
System.out.println(claims);
}
运行测试方法:
{id=1, exp=1672729730}
令牌解析后,我们可以看到id和过期时间,如果在解析的过程当中没有报错,就说明解析成功了。
下面我们做一个测试:将Token进行修改,观察是否解析失败:

结论: 篡改令牌中的任何一个字符,在对令牌进行解析时都会报错,所以JWT令牌是非常安全可靠的。

3. 测试令牌过期
我们继续测试:修改生成令牌的时指定的过期时间

总结
课堂作业
- jwt的组成部分?🎤
- JWT是如何将原始的JSON格式数据,转变为字符串的呢?🎤
- JWT令牌是非常安全可靠的,从何谈起?🎤
1.2 JWT集成代码中 ✏️ 🍐
JWT集成登陆模块
JWT令牌的生成和校验的基本操作我们已经学习完了,接下来我们就需要在案例当中通过JWT令牌技术来跟踪会话。具体的思路我们前面已经分析过了,主要就是两步操作: 👇 👇
- 生成令牌
- 在登录成功之后来生成一个JWT令牌,并且把这个令牌直接返回给前端 👈
- 校验令牌
- 拦截前端请求,从请求中获取到令牌,对令牌进行解析校验
代码实现
下图是登录成功后返回的token
下图是每次请求的时候小程序携带的token

再次一次看下流图:

下面结合获取房型的代码,分析下如何获得JWT令牌中的数据: 👇
- 由小程序的请求截图可知,在请求头上有个的authorization字段,这个字段的值就是token,我们通过这个字段的值就可以获取到token。
- 那如何获得请求头尼?需要获取请求协议对象,如下图:👇

测试图如下:

总结
课堂作业
- 通过上述的讲解,我们知道了如何在代码中获取JWT令牌中的数据?🎤
1.3. 绑定家人 ✏️
绑定家人


绑定的家人必须是已经在住的老人,这样才可以看到老人的信息,考虑到管理后端还没有写,因此绑定的老人需要从下列的列表中进行填写 👇
其中称呼,可以随便写,如叔叔,义父,爷爷等等,只要不是老人本人就行,因为老人本人是看不到这个列表的,只有家属可以看到。
序号 | 姓名 | 身份证 |
---|---|---|
1 | 刘备 | 110123195001016688 |
2 | 关羽 | 110123195101016688 |
3 | 刘爱国 | 132112196712137788 |
4 | 张飞 | 110123195201016688 |
5 | 码云 | 132122194812052345 |
6 | 张芳 | 132123195208121235 |
用户添加家人,需要搞清楚数据库表关系:多对多的关系,因此需要三张表,一张是老人表,一张是家属表,还有一个中间表,用于存储老人和家属的关系。 👇

也就是说,绑定老人,其实是添加老人和家属的关系,往中间表中(family_member_elder
)添加数据.
接下来,我们来分析下流程: 👇

好了,接下来来看下接口信息:👇
- 请求路径:
http://127.0.0.1:9995/customer/user/add
- 请求方式:
POST
- 参数类型:
application/json
- 请求参数:
{
"idCard": "110123195001016688",
"name": "刘备",
"relation": "义父"
}
- 请求参数说明:
参数名 | 类型 | 是否必填 | 说明 |
---|---|---|---|
idCard | String | 是 | 老人身份证号 |
name | String | 是 | 家属姓名 |
relation | String | 是 | 家属与老人的关系 |
- 返回值类型:
application/json
- 返回值:
{
"code": 200,
"msg": "操作成功",
"data": null
}
- 返回值说明:
参数名 | 类型 | 说明 |
---|---|---|
code | Integer | 状态码 |
msg | String | 消息 |
data | Object | 数据 |
核心代码如下:👇
1. Controller代码如下:👇
@RestController
@RequestMapping("/customer/user")
@Api(tags = "老人家属的接口")
public class FamilyMemberController {
@Autowired
ElderMapper elderMapper;
@Autowired
FamilyMemberElderMapper familyMemberElderMapper;
/**
* 新增客户老人关联记录
* @param memberElderDto 客户老人关联 DTO
* @return 操作结果
*/
@PostMapping("/add")
@ApiOperation(value = "新增客户老人关联记录")
public AjaxResult add(@RequestBody MemberElderDto memberElderDto, HttpServletRequest request) {
//根据身份证号查询老人
QueryWrapper<Elder> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id_card_no",memberElderDto.getIdCard());
Elder elder = elderMapper.selectOne(queryWrapper);
//判断老人是否存在
if(ObjectUtil.isEmpty(elder)){
return AjaxResult.error("该老人未入住,请检查输入信息");
}
//当前登录人
----这里代码需要自己补充start--
----这里代码需要自己补充start--
//判断是否已经绑定
QueryWrapper<FamilyMemberElder> queryWrapper2 = new QueryWrapper<>();
queryWrapper2.eq("elder_id",elder.getId());
queryWrapper2.eq("family_member_id",userId);
long count = familyMemberElderMapper.selectCount(queryWrapper2);
if(count > 0){
return AjaxResult.error("该老人已绑定,请勿重复绑定");
}
//保存家属与老人的关系
FamilyMemberElder familyMemberElder = BeanUtil.toBean(memberElderDto, FamilyMemberElder.class);
familyMemberElder.setElderId(elder.getId());
familyMemberElder.setFamilyMemberId(Long.valueOf(userId));
familyMemberElder.setCreateTime(new Date());
familyMemberElderMapper.insert(familyMemberElder);
return AjaxResult.success();
}}
2. 因为mapper中都有现成的方法,因此不需要写,直接调用即可。
3.测试截图:


总结
1.4. 家人列表 ✏️
前言
接下来完成家人列表的功能,如下:👇
开发功能前,先阅读接口文档,了解请求和响应数据格式,如下:👇
- 请求路径:
/customer/user/list-by-page
- 请求方式:
GET
- 请求参数:
/customer/user/list-by-page?pageNum=1&pageSize=10
- 响应数据:
application/json
- 响应数据格式:
{
"msg": "操作成功",
"code": 200,
"data": [
{
"mid": "17",//用户id
"mremark": "义父",//备注
"elderId": "3",//老人id
"name": "张飞",//老人姓名
"image": "https://itheim.oss-cn-beijing.aliyuncs.com/6528514b-e7db-468d-8aeb-590d3102125b.png",//头像
"bedNumber": "303-1",//床号
"typeName": "豪华单人间",//房间类型
"iotId": "dHMuAjTk1D9scHXtmpvrk1bok0",//设备id
"deviceName": null,//设备名称
"productKey": null//产品key
}
]
}
通过接口文档的返回值可知,内容需要涉及到多多张表,因此需要使用到多表查询,如下:👇

sql语句如下:查询的字段和实体类需要一一对应 👇
select m.id as mid
, m.remark as mremark
, m.elder_id
, e.name
, e.image
, e.bed_number
from family_member_elder m
left join elder e on m.elder_id = e.id
where m.family_member_id = 家属用户ID
Controller代码如下:👇
@GetMapping("/list-by-page")
@ApiOperation(value = "查询客户老人关联记录")
public AjaxResult listByPage(Integer pageNum, Integer pageSize,HttpServletRequest request) {
//1.从请求头中获取当前userId
-----这里代码需要自己补充start--
-----这里代码需要自己补end---
//2.通过多表查询出MemberElderVo列表
List<MemberElderVo> memberElders = familyMemberElderMapper.listByPage(userId);
//补点假数据,后期来真的数据
memberElders.forEach(memberElder -> {
memberElder.setTypeName("豪华单人间");
memberElder.setIotId("dHMuAjTk1D9scHXtmpvrk1bok0");
});
//3.返回数据
return AjaxResult.success(memberElders);
}
FamilyMemberElderMapper代码如下:
@Mapper
public interface FamilyMemberElderMapper extends BaseMapper<FamilyMemberElder> {
List<MemberElderVo> listByPage(Integer userId);
}
FamilyMemberElderMapper映射文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.yangeit.mapper.FamilyMemberElderMapper">
<resultMap id="BaseResultMap" type="cn.yangeit.pojo.FamilyMemberElder">
<id property="id" column="id" jdbcType="BIGINT"/>
<result property="familyMemberId" column="family_member_id" jdbcType="BIGINT"/>
<result property="elderId" column="elder_id" jdbcType="BIGINT"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="createBy" column="create_by" jdbcType="BIGINT"/>
<result property="updateBy" column="update_by" jdbcType="BIGINT"/>
<result property="remark" column="remark" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,family_member_id,elder_id,
create_time,update_time,create_by,
update_by,remark
</sql>
<select id="listByPage" resultType="cn.yangeit.vo.MemberElderVo">
select m.id as mid
, m.remark as mremark
, m.elder_id
, e.name
, e.image
, e.bed_number
from family_member_elder m
left join elder e on m.elder_id = e.id
where m.family_member_id = #{memberId}
</select>
</mapper>
写完逻辑后,进行重新启动项目测试如下:👇
总结
1.5. 解绑家人 ✏️
前言
好了已经完成绑定家人,家人列表,接下来就是解绑家人了,解绑家人需要提供家人ID,然后进行解绑,解绑后,家人列表中就不再显示该家人了。

老规矩,我们先从接口文档中获取接口信息:👇
- 请求路径:
/customer/user/deleteById
- 请求方式:
delete
- 请求参数:
/customer/user/deleteById?id=17
- 响应数据:
application/json
- 响应数据格式:
{
"msg": "操作成功",
"code": 200,
"data": null
}
Controller代码如下:👇
@DeleteMapping("/deleteById")
@ApiOperation(value = "根据id删除客户老人关联记录")
public AjaxResult deleteById(@RequestParam Long id) {
int result = familyMemberElderMapper.deleteById(id);
//判断是否删除成功 result是影响行数
return result>0?AjaxResult.success():AjaxResult.error();
}
删除方法Mp已经实现类,所以直接调用即可。重启后测试一下

总结