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

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

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

前言

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

需求分析

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

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

思路說明

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

developers.weixin.qq.com/miniprogram…

微信官方推薦登錄流程:

注意點(diǎn):

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

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

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

建表語(yǔ)句:

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ī)號(hào)',
"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)建時(shí)間',
"update_time" timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
"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='老人家屬';

接口說明

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

請(qǐng)求參數(shù):

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

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

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

響應(yīng)示例:

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

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

必要配置

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

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

修改請(qǐng)求路徑

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

修改小程序環(huán)境的APPID,改為自己申請(qǐng)的測(cè)試號(hào)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("登錄臨時(shí)憑證")
    private String code;
    @ApiModelProperty("手機(jī)號(hào)臨時(shí)憑證")
    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 = "登錄對(duì)象")
public class LoginVo {

    @ApiModelProperty(value = "JWT token")
    private String token;

    @ApiModelProperty(value = "昵稱")
    private String nickName;
}

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

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

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

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

新增WeachatService接口:

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

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

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

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

        return token;

    }
}

上面的代碼需要讀取獲取appIdappSecret,所以我們?cè)?code>application.yml配置對(duì)于配置。

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

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

注意:

小程序所有請(qǐng)求不走后臺(tái)的用戶,所以在新增或修改的時(shí)候,不需要自動(dòng)填充創(chuàng)建人和修改人,修改MP的自動(dòng)填充。

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

思路分析

用戶登錄成功之后,返回前端一個(gè)token,這個(gè)token就是用來驗(yàn)證用戶信息的,用戶點(diǎn)擊小程序中的其他操作,就會(huì)token攜帶請(qǐng)求頭header中,方便后臺(tái)去驗(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)前請(qǐ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ù)存儲(chǔ)到線程中
        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是用戶在這個(gè)小程序的唯一標(biāo)識(shí),unionId是微信是你在微信開發(fā)平臺(tái)的唯一標(biāo)識(shí),就是多個(gè)小程序中你的unionId都是一樣的。
  • • 前端wx.login獲取臨時(shí)登錄code,傳給后端,后端用來?yè)Q取openId。
  • • 獲取手機(jī)號(hào)需要先獲取token,然后再去獲取手機(jī)號(hào)。

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

相關(guān)文章

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

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

    在Java中List是一個(gè)有序集合,允許存儲(chǔ)重復(fù)的元素,它是Java集合框架的一部分,提供了對(duì)集合進(jìn)行各種操作的接口,這篇文章主要介紹了Java中List10個(gè)坑的相關(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包裝類之自動(dòng)裝箱與拆箱

    Java包裝類之自動(dòng)裝箱與拆箱

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

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

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

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

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

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

    ActiveMQ是一種開源的基于JMS規(guī)范的一種消息中間件的實(shí)現(xiàn),ActiveMQ的設(shè)計(jì)目標(biāo)是提供標(biāo)準(zhǔn)的,面向消息的,能夠跨越多語(yǔ)言和多系統(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)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • java壓縮文件和下載圖片示例

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

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

最新評(píng)論

特克斯县| 苍溪县| 青川县| 洪雅县| 九江县| 茂名市| 海门市| 闸北区| 贵阳市| 宝鸡市| 阳泉市| 积石山| 库尔勒市| 镇赉县| 承德市| 新竹市| 剑河县| 财经| 高台县| 屏东市| 永登县| 班戈县| 崇信县| 福建省| 永昌县| 伊宁县| 连城县| 米林县| 浦东新区| 喜德县| 仪征市| 工布江达县| 姜堰市| 牟定县| 通许县| 玛多县| 昭平县| 赤城县| 屏南县| 兖州市| 游戏|