0


基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(九)

微信登录

      1. HttpClient- - 1.1 入门- - 1.1.1 入门案例- 1.2.1 GET方式请求- 1.2.2 POST方式请求- 1.2 微信登录流程- 1.3 需求分析和设计- - 1.3.1 产品原型- 1.3.2 接口设计- 1.3.3 表设计- 1.4 代码开发- - 1.4.1 定义相关配置- 1.4.2 DTO设计- 1.4.3 VO设计- 1.4.4 Controller层- 1.4.5 Service层接口- 1.4.6 Service层实现类- 1.4.7 Mapper层- 1.4.8 编写拦截器- 1.5 功能测试

1. HttpClient

1.1 入门

在进行微信登录之前我们要先知道HttpClient是什么,它是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

在这里插入图片描述

HttpClient作用:

  • 发送HTTP请求
  • 接收响应数据

HttpClient应用场景:

当我们在使用扫描支付、查看地图、获取验证码、查看天气等功能时

在这里插入图片描述

其实,应用程序本身并未实现这些功能,都是在应用程序里访问提供这些功能的服务,访问这些服务需要发送HTTP请求,并且接收响应数据,可通过HttpClient来实现。

HttpClient的maven坐标:

<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version></dependency>

HttpClient的核心API:

  • HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。
  • HttpClients:可认为是构建器,可创建HttpClient对象。
  • CloseableHttpClient:实现类,实现了HttpClient接口。
  • HttpGet:Get方式请求类型。
  • HttpPost:Post方式请求类型。

HttpClient发送请求步骤:

  • 创建HttpClient对象
  • 创建Http请求对象
  • 调用HttpClient的execute方法发送请求
1.1.1 入门案例
1.2.1 GET方式请求

进入到sky-server模块,编写测试代码,发送GET请求。

实现步骤:

  1. 创建HttpClient对象
  2. 创建请求对象
  3. 发送请求,接受响应结果
  4. 解析结果
  5. 关闭资源
packagecom.sky.test;@SpringBootTestpublicclassHttpClientTest{/**
     * 测试通过httpclient发送GET方式的请求
     */@TestpublicvoidtestGET()throwsException{//创建httpclient对象CloseableHttpClient httpClient =HttpClients.createDefault();//创建请求对象HttpGet httpGet =newHttpGet("http://localhost:8080/user/shop/status");//发送请求,接受响应结果CloseableHttpResponse response = httpClient.execute(httpGet);//获取服务端返回的状态码int statusCode = response.getStatusLine().getStatusCode();System.out.println("服务端返回的状态码为:"+ statusCode);HttpEntity entity = response.getEntity();String body =EntityUtils.toString(entity);System.out.println("服务端返回的数据为:"+ body);//关闭资源
        response.close();
        httpClient.close();}}

在访问http://localhost:8080/user/shop/status请求时,需要提前启动项目。

测试结果:

在这里插入图片描述

1.2.2 POST方式请求

在HttpClientTest中添加POST方式请求方法,相比GET请求来说,POST请求若携带参数需要封装请求体对象,并将该对象设置在请求对象中。

实现步骤:

  1. 创建HttpClient对象
  2. 创建请求对象
  3. 发送请求,接收响应结果
  4. 解析响应结果
  5. 关闭资源
/**
     * 测试通过httpclient发送POST方式的请求
     */@TestpublicvoidtestPOST()throwsException{// 创建httpclient对象CloseableHttpClient httpClient =HttpClients.createDefault();//创建请求对象HttpPost httpPost =newHttpPost("http://localhost:8080/admin/employee/login");JSONObject jsonObject =newJSONObject();
        jsonObject.put("username","admin");
        jsonObject.put("password","123456");StringEntity entity =newStringEntity(jsonObject.toString());//指定请求编码方式
        entity.setContentEncoding("utf-8");//数据格式
        entity.setContentType("application/json");
        httpPost.setEntity(entity);//发送请求CloseableHttpResponse response = httpClient.execute(httpPost);//解析返回结果int statusCode = response.getStatusLine().getStatusCode();System.out.println("响应码为:"+ statusCode);HttpEntity entity1 = response.getEntity();String body =EntityUtils.toString(entity1);System.out.println("响应数据为:"+ body);//关闭资源
        response.close();
        httpClient.close();}

测试结果:

在这里插入图片描述

1.2 微信登录流程

微信登录:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

流程图:

在这里插入图片描述

步骤分析:

  1. 小程序端,调用wx.login()获取code,就是授权码。
  2. 小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。
  3. 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。
  4. 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。
  5. 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。
  6. 小程序端,收到自定义登录态,存储storage。
  7. 小程序端,后绪通过wx.request()发起业务请求时,携带token。
  8. 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。
  9. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

接下来,使用Postman进行测试。

说明:

  1. 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  2. 调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key

之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

实现步骤:

1). 获取授权码

点击确定按钮,获取授权码,每个授权码只能使用一次,每次测试,需重新获取。

在这里插入图片描述

2). 明确请求接口

请求方式、请求路径、请求参数

在这里插入图片描述

3). 发送请求

获取session_key和openid

在这里插入图片描述

若出现code been used错误提示,说明授权码已被使用过,请重新获取

在这里插入图片描述

1.3 需求分析和设计

1.3.1 产品原型

用户进入到小程序的时候,微信授权登录之后才能点餐。需要获取当前微信用户的相关信息,比如昵称、头像等,这样才能够进入到小程序进行下单操作。是基于微信登录来实现小程序的登录功能,没有采用传统账户密码登录的方式。若第一次使用小程序来点餐,就是一个新用户,需要把这个新的用户保存到数据库当中完成自动注册。

登录功能原型图:

在这里插入图片描述

业务规则:

  • 基于微信登录实现小程序的登录功能
  • 如果是新用户需要自动完成注册
1.3.2 接口设计

通过微信登录的流程,如果要完成微信登录的话,最终就要获得微信用户的openid。在小程序端获取授权码后,向后端服务发送请求,并携带授权码,这样后端服务在收到授权码后,就可以去请求微信接口服务。最终,后端向小程序返回openid和token等数据。

基于上述的登录流程,就可以设计出该接口的请求参数返回数据

在这里插入图片描述
在这里插入图片描述

说明:请求路径/user/user/login,第一个user代表用户端,第二个user代表用户模块。

1.3.3 表设计

当用户第一次使用小程序时,会完成自动注册,把用户信息存储到user表中。
字段名数据类型说明****备注idbigint主键自增openidvarchar(45)微信用户的唯一标识namevarchar(32)用户姓名phonevarchar(11)手机号sexvarchar(2)性别id_numbervarchar(18)身份证号avatarvarchar(500)微信用户头像路径create_timedatetime注册时间

1.4 代码开发

1.4.1 定义相关配置

配置微信登录所需配置项:

application-dev.yml

sky:wechat:appid:***********secret:***********************

application.yml

sky:wechat:appid: ${sky.wechat.appid}secret: ${sky.wechat.secret}

配置为微信用户生成jwt令牌时使用的配置项:

application.yml

sky:jwt:# 设置jwt签名加密时使用的秘钥admin-secret-key: itcast
    # 设置jwt过期时间admin-ttl:7200000# 设置前端传递过来的令牌名称admin-token-name: token
    user-secret-key: test
    user-ttl:7200000user-token-name: authentication
1.4.2 DTO设计

根据传入参数设计DTO类:

在这里插入图片描述

在sky-pojo模块,UserLoginDTO.java已定义

packagecom.sky.dto;/**
 * C端用户登录
 */@DatapublicclassUserLoginDTOimplementsSerializable{privateString code;}
1.4.3 VO设计

根据返回数据设计VO类:

在这里插入图片描述

在sky-pojo模块,UserLoginVO.java已定义

packagecom.sky.vo;@Data@Builder@NoArgsConstructor@AllArgsConstructorpublicclassUserLoginVOimplementsSerializable{privateLong id;privateString openid;privateString token;}
1.4.4 Controller层

根据接口定义创建UserController的login方法:

packagecom.sky.controller.user;@RestController@RequestMapping("/user/user")@Api(tags ="C端用户相关接口")@Slf4jpublicclassUserController{@AutowiredprivateUserService userService;@AutowiredprivateJwtProperties jwtProperties;/**
     * 微信登录
     * @param userLoginDTO
     * @return
     */@PostMapping("/login")@ApiOperation("微信登录")publicResult<UserLoginVO>login(@RequestBodyUserLoginDTO userLoginDTO){
        log.info("微信用户登录:{}",userLoginDTO.getCode());//微信登录User user = userService.wxLogin(userLoginDTO);//后绪步骤实现//为微信用户生成jwt令牌Map<String,Object> claims =newHashMap<>();
        claims.put(JwtClaimsConstant.USER_ID,user.getId());String token =JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);UserLoginVO userLoginVO =UserLoginVO.builder().id(user.getId()).openid(user.getOpenid()).token(token).build();returnResult.success(userLoginVO);}}

其中,JwtClaimsConstant.USER_ID常量已定义。

1.4.5 Service层接口

创建UserService接口:

packagecom.sky.service;importcom.sky.dto.UserLoginDTO;importcom.sky.entity.User;publicinterfaceUserService{/**
     * 微信登录
     * @param userLoginDTO
     * @return
     */UserwxLogin(UserLoginDTO userLoginDTO);}
1.4.6 Service层实现类

创建UserServiceImpl实现类:实现获取微信用户的openid和微信登录功能

packagecom.sky.service.impl;@Service@Slf4jpublicclassUserServiceImplimplementsUserService{//微信服务接口地址publicstaticfinalStringWX_LOGIN="https://api.weixin.qq.com/sns/jscode2session";@AutowiredprivateWeChatProperties weChatProperties;@AutowiredprivateUserMapper userMapper;/**
     * 微信登录
     * @param userLoginDTO
     * @return
     */publicUserwxLogin(UserLoginDTO userLoginDTO){String openid =getOpenid(userLoginDTO.getCode());//判断openid是否为空,如果为空表示登录失败,抛出业务异常if(openid ==null){thrownewLoginFailedException(MessageConstant.LOGIN_FAILED);}//判断当前用户是否为新用户User user = userMapper.getByOpenid(openid);//如果是新用户,自动完成注册if(user ==null){
            user =User.builder().openid(openid).createTime(LocalDateTime.now()).build();
            userMapper.insert(user);//后绪步骤实现}//返回这个用户对象return user;}/**
     * 调用微信接口服务,获取微信用户的openid
     * @param code
     * @return
     */privateStringgetOpenid(String code){//调用微信接口服务,获得当前微信用户的openidMap<String,String> map =newHashMap<>();
        map.put("appid",weChatProperties.getAppid());
        map.put("secret",weChatProperties.getSecret());
        map.put("js_code",code);
        map.put("grant_type","authorization_code");String json =HttpClientUtil.doGet(WX_LOGIN, map);JSONObject jsonObject =JSON.parseObject(json);String openid = jsonObject.getString("openid");return openid;}}
1.4.7 Mapper层

创建UserMapper接口:

packagecom.sky.mapper;@MapperpublicinterfaceUserMapper{/**
     * 根据openid查询用户
     * @param openid
     * @return
     */@Select("select * from user where openid = #{openid}")UsergetByOpenid(String openid);/**
     * 插入数据
     * @param user
     */voidinsert(User user);}

创建UserMapper.xml映射文件:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.sky.mapper.UserMapper"><insertid="insert"useGeneratedKeys="true"keyProperty="id">
        insert into user (openid, name, phone, sex, id_number, avatar, create_time)
        values (#{openid}, #{name}, #{phone}, #{sex}, #{idNumber}, #{avatar}, #{createTime})
    </insert></mapper>
1.4.8 编写拦截器

编写拦截器JwtTokenUserInterceptor:统一拦截用户端发送的请求并进行jwt校验

packagecom.sky.interceptor;/**
 * jwt令牌校验的拦截器
 */@Component@Slf4jpublicclassJwtTokenUserInterceptorimplementsHandlerInterceptor{@AutowiredprivateJwtProperties jwtProperties;/**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */publicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{//判断当前拦截到的是Controller的方法还是其他资源if(!(handler instanceofHandlerMethod)){//当前拦截到的不是动态方法,直接放行returntrue;}//1、从请求头中获取令牌String token = request.getHeader(jwtProperties.getUserTokenName());//2、校验令牌try{
            log.info("jwt校验:{}", token);Claims claims =JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);Long userId =Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
            log.info("当前用户的id:", userId);BaseContext.setCurrentId(userId);//3、通过,放行returntrue;}catch(Exception ex){//4、不通过,响应401状态码
            response.setStatus(401);returnfalse;}}}

在WebMvcConfiguration配置类中注册拦截器:

@AutowiredprivateJwtTokenUserInterceptor jwtTokenUserInterceptor;/**
     * 注册自定义拦截器
     * @param registry
     */protectedvoidaddInterceptors(InterceptorRegistry registry){
        log.info("开始注册自定义拦截器...");//.........

        registry.addInterceptor(jwtTokenUserInterceptor).addPathPatterns("/user/**").excludePathPatterns("/user/user/login").excludePathPatterns("/user/shop/status");}

1.5 功能测试

重新编译小程序,进行登录,获取到openid和token数据
在这里插入图片描述

查看后台日志

在这里插入图片描述

查看数据库user表,第一次登录,会自动注册

在这里插入图片描述

后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹


本文转载自: https://blog.csdn.net/m0_59230408/article/details/133297770
版权归原作者 失重外太空. 所有, 如有侵权,请联系我们删除。

“基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(九)”的评论:

还没有评论