微信登录接口对接
三方接口
接口文档
在项目中对接任何三方接口,最重要的参考永远都是官方给提供的接口文档
微信官方提供的微信登录接口:小程序登录 / 小程序登录
这个文档中比较关键的部分就是:

- 功能描述,查阅是否符合咱们业务对接的需求
- 调用方式,三方接口一般都是https居多,更加安全,包含了请求方式和请求地址
- 请求参数,接口的入参,重点关注必须要传的参数
- 返回参数,查看返回的数据结构和内容,是否符合自己的返回预期
- 错误码,当接口调用失败的时候,可以根据错误码对应的描述修正代码解决问题
API客户端
如果从后端调用三方接口,一样也是用的http请求,只是这次请求是**从后端到另外一个后端服务**,所以需要借助工具才能发起请求

HTTP客户端工具有很多
- HttpClient 体量庞大,jdk11以上支持不好
- OkHttp 学习成本较高
- Spring提供的 RestTemplate 后期课程feign会重点讲解
- 糊涂工具包,使用说明地址:概述 | Hutool
糊涂工具常见方法
- 准备:在zzyl-common模块中添加单元测试依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
GET请求
package com.zzyl.nursing.test;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
public class HttpTest {
@Test
public void testGet() {
String result = HttpUtil.get("https://www.baidu.com");
System.out.println(result);
}
@Test
public void testGetByParam() {
// 构建查询参数
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("pageNum", 1);
paramMap.put("pageSize", 5);
// 分页查询护理项目
String result = HttpUtil.get("http://localhost:8080/nursing/project/list", paramMap);
System.out.println(result);
}
@Test
public void testCreateRequest() {
// 构建查询参数
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("pageNum", 1);
paramMap.put("pageSize", 5);
// 分页查询护理项目
HttpResponse response = HttpUtil.createRequest(Method.GET, "http://localhost:8080/nursing/project/list")
.header("Authorization", "Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImUyNTgyNzdhLTMxNzAtNGFhNS05MmNhLWRiNjFiNDYyOGYxNCJ9.p8ieeQe4A6lozsxpzTL4Z3JNAS3UgBOVRZckDFnNvJQs8C2I5X5nZVxAQCaUv_0u9yUN-d0FKDCHwtCkMeIdBA")
.form(paramMap)
.execute();
if(response.isOk()){
System.out.println(response.body());
}
}
}
POST请求
@Test
public void testPost() {
String url = "http://localhost:8080/nursing/project";
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("name", "护理项目测试");
paramMap.put("orderNo", 1);
paramMap.put("unit", "次");
paramMap.put("price", 10.00);
paramMap.put("image", "https://yjy-slwl-oss.oss-cn-hangzhou.aliyuncs.com/ae7cf766-fb7b-49ff-a73c-c86c25f280e1.png");
paramMap.put("nursingRequirement", "无特殊要求");
paramMap.put("status", 1);
String result = HttpUtil.post(url, JSONUtil.toJsonStr(paramMap));
System.out.println(result);
}
@Test
public void testCreatePost() {
String url = "http://localhost:8080/nursing/project";
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("name", "护理项目测试");
paramMap.put("orderNo", 1);
paramMap.put("unit", "次");
paramMap.put("price", 10.00);
paramMap.put("image", "https://yjy-slwl-oss.oss-cn-hangzhou.aliyuncs.com/ae7cf766-fb7b-49ff-a73c-c86c25f280e1.png");
paramMap.put("nursingRequirement", "无特殊要求");
paramMap.put("status", 1);
// 发送请求
HttpResponse response = HttpUtil.createPost(url).body(JSONUtil.toJsonStr(paramMap))
.header("Authorization", "Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImUyNTgyNzdhLTMxNzAtNGFhNS05MmNhLWRiNjFiNDYyOGYxNCJ9.p8ieeQe4A6lozsxpzTL4Z3JNAS3UgBOVRZckDFnNvJQs8C2I5X5nZVxAQCaUv_0u9yUN-d0FKDCHwtCkMeIdBA")
.execute();
if(response.isOk()) {
System.out.println(response.body());
}
}
微信登录
原型文档: 定位在家属端原型图–>登录模块(若依版本 - 产品原型,密码:FSTI)
在任意需要登录才能显示信息的页面点击时,都会判断当前用户是否登录,如未登录,则进入登录页面
点击微信快速登录,弹出弹窗,需要获取用户手机号进行登录

微信登录业务逻辑规则:

小程序环境搭建
基础环境说明
- 在资料文件夹中找到”微信开发者工具安装包”安装到本地,也可以直接到官网下载:下载地址
- 在本章资料文件夹中找到”mp-weixin.zip”的压缩包,解压到一个没有中文和空格的目录
- 打开微信开发者工具,需要使用微信扫描登录
- 登录成功后,点击“+”号,找到刚才解压的小程序代码的目录,然后打开


打开之后,需要信任该项目,加载完毕之后的效果如下:

必要配置
- 修改请求的路径

- 需要忽略https请求

修改小程序环境的APPID
然后打开 –>详情–>基本信息–>修改APPID,改为自己申请的测试号APPID

测试前后端
- 放行小程序请求
由于小程序端单独有一套认证校验的逻辑,不需要使用spring security框架来做认证和授权,需要修改SecurityConfig类,对小程序中的所有请求放行

- 到资料中拷贝准备好的类:
MemberRoomTypeController,这个类是查询所有房型的 - 拷贝到zzyl-nursing-platform模块中,效果如下:

- 重启项目,访问小程序

微信登录思路说明
微信官方推荐登录流程:

注意点:
- 前端在小程序中集成微信相关依赖,当用户请求登录的同时,调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器
- 后端服务器调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台账号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台账号) 和 会话密钥 session_key
- 开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份
表结构说明
从小程序端登录的用户主要有两类,第一类是参观预约的用户,第二类是老人的家人(子女),方便查看老人信息、给老人下单、查看账单、查看合同等
如果是老人的家人,需要跟入住的老人进行绑定,方便后期享受更多服务,所以,咱们需要将在小程序端登录的用户保存下来,保存到一张表中 family_member(家属表)

建表语句:
CREATE TABLE "family_member" (
"id" bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
"phone" varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '手机号',
"name" varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '名称',
"avatar" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '头像',
"open_id" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'OpenID',
"gender" int DEFAULT NULL COMMENT '性别(0:男,1:女)',
"create_time" timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
"update_time" timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
"create_by" bigint DEFAULT NULL COMMENT '创建人',
"update_by" bigint DEFAULT NULL COMMENT '更新人',
"remark" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
PRIMARY KEY ("id") USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='老人家属';
接口说明
这个接口跟平时开发的接口略有不同,因为需要参考微信开发者平台提供的流程来开发
目前,小程序的代码已经开发完了,所以只需要在后台提供接口就可以了
- 接口路径(已固定):
/member/user/login - 请求方式(已固定):
POST - 请求参数:(已固定)
{
"code": "0e36jkGa1ercRF0Fu4Ia1V3fPD06jkGW", // 临时登录凭证code
"nickName": "微信用户",
"phoneCode": "13fe315872a4fb9ed3deee1e5909d5af60dfce7911013436fddcfe13f55ecad3"
}
以上三个参数,都是前端开发人员调用
wx.login()方法返回的数据
- code:临时登录凭证code
- nickName:微信用户昵称(现在微信统一返回为:微信用户)
- phoneCode:用户详细信息code,后台根据此code可以获取用户手机号
- 响应示例
{
"code": 200,
"msg": "操作成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiLlpb3mn7_lvIDoirE4OTE1IiwiZXhwIjoxNDY1MjI3MTMyOCwidXNlcmlkIjoxfQ.nB6ElZbUywh-yiHDNMJS8WqUpcLWCszVdvAMfySFxIM",
"nickName": "好柿开花8915"
},
"operationTime": null
}
功能实现
实现思路说明
基于微信官方提供的流程图,结合实际业务,最终的实现思路,如下图

基础代码准备
按照之前的套路,使用若依框架的代码生成功能,来生成代码,
- 包名:
com.zzyl.nursing - 模块名:
nursing - 除了主键之外的Long类型,改为Integer
只拷贝后端代码到idea中

控制层
在zzyl-nursing-platform模块中,把FamilyMemberController移动到member包下,结构如下:

删除里面所有的方法,新定义一个登录方法,最终代码如下:
package com.zzyl.nursing.controller.member;
import com.zzyl.common.core.controller.BaseController;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.nursing.dto.UserLoginRequestDto;
import com.zzyl.nursing.service.IFamilyMemberService;
import com.zzyl.nursing.vo.LoginVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 老人家属Controller
*
* @author ruoyi
* @date 2024-09-02
*/
@RestController
@RequestMapping("/member/user")
@Api(tags = "老人家属相关接口")
public class FamilyMemberController extends BaseController
{
@Autowired
private IFamilyMemberService familyMemberService;
@PostMapping("/login")
@ApiOperation("小程序登录")
public AjaxResult login(@RequestBody UserLoginRequestDto userLoginRequestDto) {
LoginVo loginVo = familyMemberService.login(userLoginRequestDto);
return success(loginVo);
}
}
基于接口文档创建对应的DTO和VO
UserLoginRequestDto :
package com.zzyl.nursing.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* C端用户登录
*/
@Data
public class UserLoginRequestDto {
@ApiModelProperty("昵称")
private String nickName;
@ApiModelProperty("登录临时凭证")
private String code;
@ApiModelProperty("手机号临时凭证")
private String phoneCode;
}
LoginVo:
package com.zzyl.nursing.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* LoginVO
* @author itheima
*/
@Data
@ApiModel(value = "登录对象")
public class LoginVo {
@ApiModelProperty(value = "JWT token")
private String token;
@ApiModelProperty(value = "昵称")
private String nickName;
}
业务层

需要调用两次微信开发者平台的接口来获取数据,一般像这种第三方接口的调用,通常都会将所有相关接口的调用都封装到一个单独的业务类中(Service层接口和实现类),使其更通用
分析微信开发者平台的接口,接口地址:
微信接口调用-单独封装
新增WechatService接口
package com.zzyl.nursing.service;
public interface WechatService {
/**
* 获取openid
* @param code
* @return
*/
public String getOpenid(String code);
/**
* 获取手机号
* @param detailCode
* @return
*/
public String getPhone(String detailCode);
}
新增WechatServiceImpl实现类
package com.zzyl.nursing.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zzyl.nursing.service.WechatService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class WechatServiceImpl implements WechatService {
// 登录
private static final String REQUEST_URL = "https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code";
// 获取token
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
// 获取手机号
private static final String PHONE_REQUEST_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";
@Value("${wechat.appId}")
private String appid;
@Value("${wechat.appSecret}")
private String secret;
/**
* 获取openid
* @param code
* @return
*/
@Override
public String getOpenid(String code) {
// 获取公共参数
Map<String,Object> paramMap = getAppConfig();
paramMap.put("js_code", code);
String result = HttpUtil.get(REQUEST_URL, paramMap);
// 结果是一个map
JSONObject jsonObject = JSONUtil.parseObj(result);
// 判断接口响应是否出错
if(ObjectUtil.isNotEmpty(jsonObject.getInt("errcode"))) {
throw new RuntimeException(jsonObject.getStr("errmsg"));
}
String openid = jsonObject.getStr("openid");
return openid;
}
/**
* 封装公共参数
* @return
*/
private Map<String, Object> getAppConfig() {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("appid", appid);
paramMap.put("secret", secret);
return paramMap;
}
/**
* 获取手机号
* @param detailCode
* @return
*/
@Override
public String getPhone(String detailCode) {
String token = getToken();
String url = PHONE_REQUEST_URL + token;
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("code", detailCode);
// 发起请求
String result = HttpUtil.post(url, JSONUtil.toJsonStr(paramMap));
// 是一个map
JSONObject jsonObject = JSONUtil.parseObj(result);
// 判断接口响应是否出错
if(jsonObject.getInt("errcode") != 0) {
throw new RuntimeException(jsonObject.getStr("errmsg"));
}
return jsonObject.getJSONObject("phone_info").getStr("phoneNumber");
}
/**
* 获取token
* @return
*/
private String getToken() {
Map<String, Object> paramMap = getAppConfig();
// 发起请求
String result = HttpUtil.get(TOKEN_URL, paramMap);
// 是一个map
JSONObject jsonObject = JSONUtil.parseObj(result);
// 判断接口响应是否出错
if(ObjectUtil.isNotEmpty(jsonObject.getInt("errcode"))) {
throw new RuntimeException(jsonObject.getStr("errmsg"));
}
String token = jsonObject.getStr("access_token");
return token;
}
}
由于上述代码需要读取配置来获取微信小程序的appid和appSecret ,所以需要在application.yml添加对应配置(appid和secret都是在官网中获取)
单元测试:
在zzyl-admin中创建单元测试WechatServiceImplTest
package com.zzyl.test;
import com.zzyl.nursing.service.WechatService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class WechatServiceImplTest {
@Autowired
private WechatService wechatService;
@Test
public void testGetOpenid() {
String openid = wechatService.getOpenid("0d3fPOkl2VLGbe4IACol2M0x5Q2fPOk5");
System.out.println(openid);//o3CsK6_C6f4WP9b0AxXNJOkc6q9Q
}
@Test
public void testGetPhone() {
String phone = wechatService.getPhone("ff1a2f2e61482537ea19c8e57850919051b43ffadbf78cd4fcd3a1707e0c1e14");
System.out.println(phone);
}
}
参数获取方式:在小程序端获取参数,输入到单元测试中

微信登录业务开发
在IFamilyMemberService中定义login方法
/**
* 微信登录
* @param userLoginRequestDto
* @return
*/
LoginVo login(UserLoginRequestDto userLoginRequestDto);
实现方法:
@Autowired
private WechatService wechatService;
@Autowired
private TokenService tokenService;
static List<String> DEFAULT_NICKNAME_PREFIX = ListUtil.of(
"生活更美好",
"大桔大利",
"日富一日",
"好柿开花",
"柿柿如意",
"一椰暴富",
"大柚所为",
"杨梅吐气",
"天生荔枝"
);
/**
* 小程序端登录
* @param userLoginRequestDto
* @return
*/
@Override
public LoginVo login(UserLoginRequestDto userLoginRequestDto) {
// 1.调用微信api,根据code获取openId
String openId = wechatService.getOpenid(userLoginRequestDto.getCode());
// 2.根据openId查询用户
FamilyMember familyMember = getOne(Wrappers.<FamilyMember>lambdaQuery(FamilyMember.class)
.eq(FamilyMember::getOpenId, openId));
// 3.如果用户为空,则将openId设置到对象中
if (ObjectUtil.isEmpty(familyMember)) {
familyMember = FamilyMember.builder().openId(openId).build();
}
// 4.调用微信api获取用户绑定的手机号
String phone = wechatService.getPhone(userLoginRequestDto.getPhoneCode());
// 5.保存或修改用户
saveOrUpdateFamilyMember(familyMember, phone);
// 6.将用户id存入token,返回
Map<String, Object> claims = new HashMap<>();
claims.put("userId", familyMember.getId());
claims.put("nickName", familyMember.getName());
String token = tokenService.createToken(claims);
LoginVo loginVo = new LoginVo();
loginVo.setToken(token);
loginVo.setNickName(familyMember.getName());
return loginVo;
}
/**
* 保存或修改客户
* @param member
* @param phone
*/
private void saveOrUpdateFamilyMember(FamilyMember member, String phone) {
// 1.判断取到的手机号与数据库中保存的手机号不一样
if(ObjectUtil.notEqual(phone, member.getPhone())) {
// 修改手机号
member.setPhone(phone);
}
// 2.判断id存在,则修改
if (ObjectUtil.isNotEmpty(member.getId())) {
updateById(member);
return;
}
// 3.id不存在,保存新用户
// 随机组装昵称,词组+手机号后四位
String nickName = DEFAULT_NICKNAME_PREFIX.get((int) (Math.random() * DEFAULT_NICKNAME_PREFIX.size()))
+ StringUtils.substring(member.getPhone(), 7);
member.setName(nickName);
save(member);
}
其他补充
- 在构建Member对象的时候,用到了构建则设计模式,参考文档:构建者设计模式
在FamilyMember添加三个注解,如下图:

- 使用TokenService生成token
在zzyl-nursing-platform中导入zzyl-framework包
<dependency>
<groupId>com.zzyl</groupId>
<artifactId>zzyl-framework</artifactId>
</dependency>
创建JWT的token的时候,可以使用项目中自带的TokenService中的方法createToken该方法的访问权限为private,需要将其设置为public

- 修改MP自动填充拦截器
由于小程序端的所有请求不走后台的用户,在新增或修改的时候,不需要自动填充创建人和修改人
package com.zzyl.framework.interceptor;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.zzyl.common.core.domain.model.LoginUser;
import com.zzyl.common.utils.SecurityUtils;
import lombok.SneakyThrows;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Autowired
private HttpServletRequest request;
@SneakyThrows
public boolean isExclude() {
String requestURI = request.getRequestURI();
if(requestURI.startsWith("/member")) {
return true;
}
return false;
}
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
if(!isExclude()) {
this.strictInsertFill(metaObject, "createBy", String.class, loadUserId() + "");
}
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
if(!isExclude()) {
this.setFieldValByName("updateBy", loadUserId() + "", metaObject);
}
}
/**
* 获取当前登录人的ID
*
* @return
*/
private static Long loadUserId() {
// 获取当前登录人的id
try {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtils.isNotEmpty(loginUser)) {
return loginUser.getUserId();
}
return 1L;
} catch (Exception e) {
return 1L;
}
}
}
校验token
思路分析
通过刚才的流程,用户登录成功以后,会给前端返回一个token,这个token就是来验证用户信息的,当用户点击了小程序中的其他操作(需要登录),则会把token携带到请求头(header)中,方便后台去验证并获取用户信息,简易流程如下

如果想要验证用户的token,可以使用自定义的拦截器实现,整体的流程如下:

Threadlocal的使用
详细请查看文档:ThreadLocal
把资料中提供的工具类UserThreadLocal拷贝到zzyl-common模块中

功能实现
在zzyl-framework模块中定义拦截器MemberInterceptor

代码如下:
package com.zzyl.framework.interceptor;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.common.utils.UserThreadLocal;
import com.zzyl.framework.web.service.TokenService;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MemberInterceptor implements HandlerInterceptor {
private final TokenService tokenService;
public MemberInterceptor(TokenService tokenService) {
this.tokenService = tokenService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果不是Controller层的请求,直接放行
if (!(handler instanceof HandlerMethod)) {
return true;
}
// 获取header中的token
String token = request.getHeader("authorization");
// 如果token为空,响应401,重新登录
if (StringUtils.isEmpty(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
throw new BaseException("认证失败");
}
// 解析token
Claims claims = tokenService.parseToken(token);
if (ObjectUtil.isEmpty(claims)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
throw new BaseException("认证失败");
}
// 获取token中的数据
Long userId = MapUtil.get(claims, "userId", Long.class);
if (ObjectUtil.isEmpty(userId)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
throw new BaseException("认证失败");
}
// 将用户id放入ThreadLocal中
UserThreadLocal.set(userId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserThreadLocal.remove();
}
}
注意,在上述代码中咱们使用了tokenService来解析token,其中的parseToken方法需要修改为public才可以
使拦截器生效
找到ResourcesConfig类,这里面定义了拦截器的生效规则,如下图

详细的代码如下:
package com.zzyl.framework.config;
import java.util.concurrent.TimeUnit;
import com.zzyl.framework.interceptor.MemberInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.zzyl.common.config.RuoYiConfig;
import com.zzyl.common.constant.Constants;
import com.zzyl.framework.interceptor.RepeatSubmitInterceptor;
/**
* 通用配置
*
* @author ruoyi
*/
@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{
@Autowired
private RepeatSubmitInterceptor repeatSubmitInterceptor;
@Autowired
private MemberInterceptor membersInterceptor;
private static final String[] EXCLUDE_PATH_PATTERNS = new String[] {
"/member/user/login",
"/member/roomTypes"
};
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
/** 本地文件上传路径 */
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
.addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;
}
/**
* 自定义拦截规则
*/
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
registry.addInterceptor(membersInterceptor).excludePathPatterns(EXCLUDE_PATH_PATTERNS).addPathPatterns("/member/**");
}
/**
* 跨域配置
*/
@Bean
public CorsFilter corsFilter()
{
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern("*");
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 有效期 1800秒
config.setMaxAge(1800L);
// 添加映射路径,拦截一切请求
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
// 返回新的CorsFilter
return new CorsFilter(source);
}
}
想要测试当前拦截器是否生效,可以随便定义一个控制层代码(需符合拦截要求的),测试代码如下:
package com.zzyl.nursing.controller.member;
import com.zzyl.common.core.controller.BaseController;
import com.zzyl.common.core.domain.R;
import com.zzyl.common.utils.UserThreadLocal;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 预约信息Controller
*
* @author ruoyi
*/
@RestController
@RequestMapping("/member/reservation")
@Api(tags = "预约信息相关接口")
public class MemberReservationController extends BaseController
{
@GetMapping("/cancelled-count")
@ApiOperation("查询取消预约数量")
public R<Integer> getCancelledReservationCount() {
Long userId = UserThreadLocal.getUserId();
System.out.println("------" + userId);
return R.ok(1);
}
}