最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot實(shí)現(xiàn)微信登錄的完整步驟流程

 更新時間:2026年06月11日 10:26:00   作者:神奇小湯圓  
隨著微信開放平臺不斷完善,越來越多的企業(yè)級應(yīng)用選擇在Web端通過微信賬號實(shí)現(xiàn)登錄,既提升用戶體驗(yàn)又便于賬號關(guān)聯(lián),這篇文章主要介紹了SpringBoot實(shí)現(xiàn)微信登錄的完整步驟流程,需要的朋友可以參考下

前言

小程序登錄在開發(fā)中是最常見的需求,哪怕小程序登錄不是你做,你還是要了解一下流程,后續(xù)都要使用到openId和unionId,你需要知道這些是干什么的。

需求分析

點(diǎn)擊登錄會彈出彈窗,需要獲取用戶手機(jī)號進(jìn)行登錄。

微信登錄業(yè)務(wù)邏輯規(guī)則:

思路說明

參考微信官方文檔的提供的思路,官方文檔:

developers.weixin.qq.com/miniprogram…

微信官方推薦登錄流程:

注意點(diǎn):

  • • 前端在小程序集成微信相關(guān)依賴,調(diào)用wx.login獲取臨時登錄憑證code,傳給后端。
  • • 后端調(diào)用auth.code2Session接口,換取openId和、UnionId、會話秘鑰Session_Key。
  • • 開發(fā)者服務(wù)器可以根據(jù)用戶標(biāo)識自定義登錄狀態(tài),用于后續(xù)業(yè)務(wù)邏輯中前后端交互識別用戶身份。

表結(jié)構(gòu)說明

創(chuàng)建一張表,用于存儲用戶的信息以及oenId。

建表語句:

CREATE TABLE "family_member" (
"id" bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵',
"phone" varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '手機(jī)號',
"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 '創(chuàng)建時間',
"update_time" timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
"create_by" bigint DEFAULT NULL COMMENT '創(chuàng)建人',
"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='老人家屬';

接口說明

接口跟平時的接口略有不同,參考微信開發(fā)者平臺提供的流程開發(fā)。

請求參數(shù):

{
  "code": "0e36jkGa1ercRF0Fu4Ia1V3fPD06jkGW", //臨時登錄憑證code
  "nickName": "微信用戶",
  "phoneCode": "13fe315872a4fb9ed3deee1e5909d5af60dfce7911013436fddcfe13f55ecad3"
}

以上三個參數(shù)都是前端調(diào)用wx.login獲取返回的參數(shù)

  • • code:  臨時登錄憑證code(有效時間5分鐘)
  • • nickName:  微信用戶昵稱(現(xiàn)在統(tǒng)一返回:微信用戶)
  • • phoneCode:  詳細(xì)用戶信息code,后臺根據(jù)此code獲取手機(jī)號。

響應(yīng)示例:

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiLlpb3mn7_lvIDoirE4OTE1IiwiZXhwIjoxNDY1MjI3MTMyOCwidXNlcmlkIjoxfQ.nB6ElZbUywh-yiHDNMJS8WqUpcLWCszVdvAMfySFxIM",
    "nickName": "好柿開花8915"
  },
  "operationTime": null
}

小程序環(huán)境搭建

必要配置

測試階段使用測試號,在微信小程序后臺獲取appId和小程序秘鑰,前端和后端都需要這兩個參數(shù)。

基礎(chǔ)環(huán)境說明

修改請求路徑

本地開發(fā)忽略https校驗(yàn)

修改小程序環(huán)境的APPID,改為自己申請的測試號APPID。

功能實(shí)現(xiàn)

實(shí)現(xiàn)思路

控制層

Controller:

@PostMapping("/login")
@ApiOperation("小程序登錄")
public AjaxResult login(@RequestBody UserLoginRequestDto userLoginRequestDto){
    LoginVo loginVo = familyMemberService.login(userLoginRequestDto);
    return success(loginVo);

}

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("手機(jī)號臨時憑證")
    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;
}

業(yè)務(wù)層【重要】

一般像這種三方接口調(diào)用,通常會封裝一個單獨(dú)業(yè)務(wù)代碼,使其更通用。

  • • 獲取用戶openId
  • • 獲取手機(jī)號
  • • 獲取token(獲取手機(jī)號需要)

微信接口調(diào)用-單獨(dú)封裝

新增WeachatService接口:

package com.zzyl.nursing.service;
 
public interface WechatService {

    /**
     * 獲取openid
     * @param code
     * @return
     */
    public String getOpenid(String code);

    /**
     * 獲取手機(jī)號
     * @param detailCode
     * @return
     */
    public String getPhone(String detailCode);
}

新增WeachatServiceImpl實(shí)現(xiàn)類:

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";

    // 獲取手機(jī)號
    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) {

        //獲取公共參數(shù)
        Map<String,Object> paramMap = getAppConfig();
        paramMap.put("js_code",code);

        String result = HttpUtil.get(REQUEST_URL, paramMap);
        //是一個map
        JSONObject jsonObject = JSONUtil.parseObj(result);
        //判斷接口響應(yīng)是否出錯
        if(ObjectUtil.isNotEmpty(jsonObject.getInt("errcode"))){
            throw new RuntimeException(jsonObject.getStr("errmsg"));
        }

        String openid = jsonObject.getStr("openid");

        return openid;
    }

    /**
     * 封裝公共參數(shù)
     * @return
     */
    private Map<String, Object> getAppConfig() {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("appid",appid);
        paramMap.put("secret",secret);
        return paramMap;
    }

    /**
     * 獲取手機(jī)號
     * @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);
        //發(fā)起請求
        String result = HttpUtil.post(url, JSONUtil.toJsonStr(paramMap));
        //是一個map
        JSONObject jsonObject = JSONUtil.parseObj(result);
        //判斷接口響應(yīng)是否出錯
        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();
        //發(fā)起請求
        String result = HttpUtil.get(TOKEN_URL, paramMap);
        //是一個map
        JSONObject jsonObject = JSONUtil.parseObj(result);
        //判斷接口響應(yīng)是否出錯
        if(ObjectUtil.isNotEmpty(jsonObject.getInt("errcode"))){
            throw new RuntimeException(jsonObject.getStr("errmsg"));
        }

        String token = jsonObject.getStr("access_token");

        return token;

    }
}

上面的代碼需要讀取獲取appIdappSecret,所以我們在application.yml配置對于配置。

微信登錄業(yè)務(wù)開發(fā)

/**
 * 微信登錄
 * @param userLoginRequestDto
 * @return
 */
LoginVo login(UserLoginRequestDto userLoginRequestDto);

實(shí)現(xiàn)方法:

@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.調(diào)用微信api,根據(jù)code獲取openId
    String openId = wechatService.getOpenid(userLoginRequestDto.getCode());

    //2.根據(jù)openId查詢用戶
    FamilyMember familyMember = getOne(Wrappers.<FamilyMember>lambdaQuery(FamilyMember.class)
            .eq(FamilyMember::getOpenId, openId));

    //3.如果用戶為空,則新增
    if (ObjectUtil.isEmpty(familyMember)) {
        familyMember = FamilyMember.builder().openId(openId).build();
    }

    //4.調(diào)用微信api獲取用戶綁定的手機(jī)號
    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("userName", 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.判斷取到的手機(jī)號與數(shù)據(jù)庫中保存的手機(jī)號不一樣
    if(ObjectUtil.notEqual(phone, member.getPhone())){
        //設(shè)置手機(jī)號
        member.setPhone(phone);
    }
    //2.判斷id存在
    if (ObjectUtil.isNotEmpty(member.getId())) {
        updateById(familyMember);
        return;
    }
    //3.保存新的用戶
    //隨機(jī)組裝昵稱,詞組+手機(jī)號后四位
    String nickName = DEFAULT_NICKNAME_PREFIX.get((int) (Math.random() * DEFAULT_NICKNAME_PREFIX.size()))
            + StringUtils.substring(member.getPhone(), 7);

    member.setName(nickName);
    save(member);
}

注意:

小程序所有請求不走后臺的用戶,所以在新增或修改的時候,不需要自動填充創(chuàng)建人和修改人,修改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")){
            returnfalse;
        }
        returntrue;
    }

    @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);
        }

    }

    /**
     * 獲取當(dāng)前登錄人的ID
     *
     * @return
     */
    private static Long loadUserId() {

        //獲取當(dāng)前登錄人的id
        try {
            LoginUser loginUser = SecurityUtils.getLoginUser();
            if (ObjectUtils.isNotEmpty(loginUser)) {
                return loginUser.getUserId();
            }
            return 1L;
        } catch (Exception e) {
            return 1L;
        }
    }
}

校驗(yàn)Toeken

思路分析

用戶登錄成功之后,返回前端一個token,這個token就是用來驗(yàn)證用戶信息的,用戶點(diǎn)擊小程序中的其他操作,就會token攜帶請求頭header中,方便后臺去驗(yàn)證獲取用戶信息,流程如下:

如果要驗(yàn)證用戶的token,我們可以使用攔截器實(shí)現(xiàn)。

代碼如下:

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 org.springframework.beans.factory.annotation.Autowired;
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;
import java.util.Map;

@Component
public class MemberInterceptor implements HandlerInterceptor {

    @Autowired
    private TokenService tokenService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //判斷當(dāng)前請求是否是handler()
        if(!(handler instanceof HandlerMethod)){
            returntrue;
        }

        //獲取token
        String token = request.getHeader("authorization");
        if(StringUtils.isEmpty(token)){
            throw new BaseException("認(rèn)證失敗");
        }
        //解析token
        Map<String, Object> claims =  tokenService.parseToken(token);
        if(ObjectUtil.isEmpty(claims)){
            throw new BaseException("認(rèn)證失敗");
        }
        Long userId = MapUtil.get(claims, "userId", Long.class);
        if(ObjectUtil.isEmpty(userId)){
            throw new BaseException("認(rèn)證失敗");
        }
        //把數(shù)據(jù)存儲到線程中
        UserThreadLocal.set(userId);
        returntrue;

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserThreadLocal.remove();
    }
}

使攔截器生效(WebMvcConfigurer實(shí)現(xiàn)類):

/**
 * 自定義攔截規(guī)則
 */
@Override
public void addInterceptors(InterceptorRegistry registry)
{
    registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
    registry.addInterceptor(membersInterceptor).excludePathPatterns(EXCLUDE_PATH_PATTERNS).addPathPatterns("/member/**");
}

總結(jié)

  • • openId是用戶在這個小程序的唯一標(biāo)識,unionId是微信是你在微信開發(fā)平臺的唯一標(biāo)識,就是多個小程序中你的unionId都是一樣的。
  • • 前端wx.login獲取臨時登錄code,傳給后端,后端用來換取openId。
  • • 獲取手機(jī)號需要先獲取token,然后再去獲取手機(jī)號。

到此這篇關(guān)于SpringBoot實(shí)現(xiàn)微信登錄的文章就介紹到這了,更多相關(guān)SpringBoot實(shí)現(xiàn)微信登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一篇文章細(xì)數(shù)Java中List的10個坑

    一篇文章細(xì)數(shù)Java中List的10個坑

    在Java中List是一個有序集合,允許存儲重復(fù)的元素,它是Java集合框架的一部分,提供了對集合進(jìn)行各種操作的接口,這篇文章主要介紹了Java中List10個坑的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-11-11
  • Java內(nèi)部類和異常類的概念以及使用

    Java內(nèi)部類和異常類的概念以及使用

    這篇文章主要介紹了Java內(nèi)部類和異常類的概念以及使用,文中有非常詳細(xì)的代碼以及注釋,適合正在學(xué)習(xí)java基礎(chǔ)的同學(xué)們使用,需要的朋友可以參考下
    2021-04-04
  • jdk在centos中安裝配置圖文教程

    jdk在centos中安裝配置圖文教程

    這篇文章主要介紹了jdk在centos中安裝配置圖文教程,文中給出大家jdk下載地址,需要的朋友可以參考下
    2018-04-04
  • Java包裝類之自動裝箱與拆箱

    Java包裝類之自動裝箱與拆箱

    這篇文章主要介紹了Java包裝類之自動裝箱與拆箱,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • mybatis plus自動生成代碼tinyint(1)自動轉(zhuǎn)換為Boolean的問題及解決

    mybatis plus自動生成代碼tinyint(1)自動轉(zhuǎn)換為Boolean的問題及解決

    這篇文章主要介紹了mybatis plus自動生成代碼tinyint(1)自動轉(zhuǎn)換為Boolean的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • 圖文詳解Java中的序列化機(jī)制

    圖文詳解Java中的序列化機(jī)制

    java中的序列化可能大家像我一樣都停留在實(shí)現(xiàn)Serializable接口上,對于它里面的一些核心機(jī)制沒有深入了解過。本文將通過示例帶大家深入了解Java中的序列化機(jī)制,需要的可以參考一下
    2022-10-10
  • spring?boot學(xué)習(xí)筆記之操作ActiveMQ指南

    spring?boot學(xué)習(xí)筆記之操作ActiveMQ指南

    ActiveMQ是一種開源的基于JMS規(guī)范的一種消息中間件的實(shí)現(xiàn),ActiveMQ的設(shè)計目標(biāo)是提供標(biāo)準(zhǔn)的,面向消息的,能夠跨越多語言和多系統(tǒng)的應(yīng)用集成消息通信中間件,這篇文章主要給大家介紹了關(guān)于spring?boot學(xué)習(xí)筆記之操作ActiveMQ指南的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • Java解決約瑟夫問題代碼實(shí)例

    Java解決約瑟夫問題代碼實(shí)例

    這篇文章主要介紹了Java解決約瑟夫(環(huán))問題的代碼實(shí)例,決約瑟問題貌似經(jīng)常出現(xiàn)在面試題中,需要的朋友可以參考下
    2014-03-03
  • java之swing下拉菜單實(shí)現(xiàn)方法

    java之swing下拉菜單實(shí)現(xiàn)方法

    這篇文章主要介紹了java之swing下拉菜單實(shí)現(xiàn)方法,以實(shí)例形式較為詳細(xì)的分析了基于swing的下拉菜單實(shí)現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-09-09
  • java壓縮文件和下載圖片示例

    java壓縮文件和下載圖片示例

    這篇文章主要為大家詳細(xì)介紹了java壓縮文件和下載圖片示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10

最新評論

锦屏县| 喀喇| 普兰店市| 佛冈县| 犍为县| 滨海县| 荆门市| 阳西县| 万山特区| 樟树市| 庆安县| 绿春县| 新源县| 松潘县| 长葛市| 南靖县| 岐山县| 香河县| 双柏县| 柘城县| 青海省| 景洪市| 正定县| 大田县| 台南市| 东乡族自治县| 桃源县| 浮山县| 巴彦县| 永川市| 大冶市| 辽宁省| 鞍山市| 白山市| 阳信县| 乡城县| 锦屏县| 西宁市| 京山县| 建宁县| 晴隆县|