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

微信小程序使用uni-app和springboot實現(xiàn)一鍵登錄功能(JWT鑒權(quán))

 更新時間:2023年11月10日 10:31:44   作者:Mao.O  
微信一鍵登錄是指用戶在使用小程序時,可以通過微信賬號進行快速登錄,而無需額外的注冊和密碼設(shè)置,這篇文章主要給大家介紹了關(guān)于微信小程序使用uni-app和springboot實現(xiàn)一鍵登錄功能的相關(guān)資料,需要的朋友可以參考下

概述

本篇博本主要為了記錄使用uni-app開發(fā)微信小程序時實現(xiàn)微信一鍵登錄功能,并且使用JWT實現(xiàn)身份認證。

微信登錄接口說明

可以點擊==>官方的登錄時序圖<== ,看到官方描述登錄的流程。

大概描述就是 :uni-app調(diào)用login()方法,獲取臨時憑證code,將此臨時憑證發(fā)送到我們的后端,后端通過我們傳入的臨時憑證code,調(diào)用微信接口服務獲取當前微信用戶的唯一標識openid,我們就可以憑借此openid知道哪一個用戶在進行登錄操作。

值得注意的是

1.通過login()獲取的臨時憑證code的有效期為5分鐘,并且只能使用一次。

2.后端調(diào)用微信憑證驗證接口獲取openid需要appIdappSecret,兩者都可以到微信小程序官網(wǎng)==>開發(fā)管理==>開發(fā)設(shè)置 中獲取。

如下是我畫的整體大概流程

總體說明 整個流程就是當用戶點擊"微信一鍵登錄",傳入臨時憑證code,后端通過臨時憑證code去微信服務接口獲取該用戶的openid,此openid是唯一的不會變的。 那么我們就可以將openid存儲用戶數(shù)據(jù)表中,用來標識此用戶。

關(guān)于獲取微信用戶的信息

關(guān)于API:uni.getUserInfo(OBJECT) 和 uni.getUserProfile(OBJECT) 接口的說明。

目前兩個接口都無法獲取用戶信息,只能獲取到默認用戶信息,名稱為'微信用戶',頭像為灰色用戶頭像。

關(guān)于官方描述:==>原文<==

 那么解決方案只能是用戶登錄后,讓用戶自行上傳修改信息。

前端代碼(uni-app)

 前端的代碼很簡單,只是調(diào)用uni.login()獲取臨時憑證code傳入后端接口即可。

<template>
	<view>
		<button id="loginHanle" @tap="goLogin">微信一鍵登錄</button>
	</view>
</template>
<script>
	export default {
		methods: {
			// 登錄按鈕觸發(fā)
			loginHanle() {
			    // 獲取臨時登錄憑證code。
			    uni.login({
			        provider: 'weixin',
			        success(res) {
                        console.log(res.code);
						// 調(diào)用后端接口,傳入code
			            axios.post('http://localhost:8888/api/userInfo/login',{code:res.code})
.then(res=>{
    // 登錄成功后的邏輯處理
    ...
})
			        }
			    })
		}
	}
</script>

后端代碼(SpringBoot)

后端需要接收前端傳入的臨時憑證code,向微信服務器發(fā)送請求獲取登錄用戶的openid。并且操作數(shù)據(jù)庫后返回用戶信息,以及響應頭返回token。

配置文件:application.yml 

# JWT配置
jwt:
  header: "Authorization" #token返回頭部
  tokenPrefix: "Bearer " #token前綴
  secret: "maohe101" #密鑰
  expireTime: 3600000 #token有效時間 3600000毫秒 ==> 60分鐘
# 微信小程序配置碼
APPID: 自己的appid
APPSECRET: 自己的密匙

配置文件:Pom.xml 

添加需要依賴 

<!-- jwt支持 -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.19.2</version>
</dependency>
 
<!-- json格式化 -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.9</version>
</dependency>

類:WeChatModel  

接收前端傳入?yún)?shù)

package com.mh.common;
import lombok.Data;
/**
 * Date:2023/5/24
 * author:zmh
 * description: 接收小程序傳入?yún)?shù)
 **/
@Data
public class WeChatModel {
    /**
     * 臨時登錄憑證
     */
    private String code;
    /**
     * 微信服務器上的唯一id
     */
    private String openId;
}

 類:WeChatSessionModel

接收調(diào)用微信驗證code后返回的數(shù)據(jù)。

package com.mh.common;
import lombok.Data;
/**
 * Date:2023/5/24
 * author:zmh
 * description: 接收微信服務器返回參數(shù)
 **/
@Data
public class WeChatSessionModel {
    /**
     * 微信服務器上辨識用戶的唯一id
     */
    private String openid;
    /**
     * 身份憑證
     */
    private String session_key;
    /**
     * 錯誤代碼
     */
    private String errcode;
    /**
     * 錯誤信息
     */
    private String errmsg;
}

 類:UserInfoController

接收臨時憑證code,調(diào)用業(yè)務層方法

@Autowired
private UserInfoService userInfoService;
 
/**
 * 微信登錄
 * @param weChatModel 獲取臨時憑證code
 * @param response ·
 * @return 返回執(zhí)行結(jié)果
 */
@PostMapping("/login")
public R<String> loginCheck(@RequestBody WeChatModel weChatModel, HttpServletResponse response){
    // 檢查登錄
    Map<String, Object> resultMap = userInfoService.checkLogin(weChatModel.getCode());
    // resultMap大于1為通過,業(yè)務層判斷正確后返回用戶信息和token,所以應該size為2才正確。
    if (resultMap.size() > 1){
        log.info("創(chuàng)建的token為=>{}", resultMap.get("token"));
        // 將token添加入響應頭以及返回用戶信息
        response.setHeader(JWTUtils.header, (String) resultMap.get("token"));
        return R.success(resultMap.get("user").toString());
    }else{
        // 當返回map的size為1時,即為報錯信息
        return R.error(resultMap.get("errmsg").toString());
    }
}

業(yè)務層實現(xiàn)類:UserInfoServiceImpl

登錄驗證的邏輯處理 

package com.mh.service.impl;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.mh.common.R;
import com.mh.common.WeChatSessionModel;
import com.mh.common.WeChatModel;
import com.mh.dao.FansInfoDao;
import com.mh.dao.FollowInfoDao;
import com.mh.dao.UserInfoDao;
import com.mh.pojo.FansInfo;
import com.mh.pojo.FollowInfo;
import com.mh.pojo.UserInfo;
import com.mh.service.UserInfoService;
import com.mh.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
 
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
 
/**
 * Date:2023/5/24
 * author:zmh
 * description: 用戶信息業(yè)務層實現(xiàn)類
 **/
 
@Service
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoDao, UserInfo> implements UserInfoService {
 
    @Autowired
    private UserInfoDao userInfoDao;
 
    @Value("${APPID}")
    private String appid;
 
    @Value("${APPSECRET}")
    private String appsecret;
 
    @Autowired
    private RestTemplate restTemplate;
	
	// 用于存儲用戶信息和token
    Map<String,Object> map = new HashMap<>();
 
    /**
     * 登錄驗證
     * @param code 臨時登錄碼
     * @return ·
     */
    public Map<String,Object> checkLogin(String code){
        // 根據(jù)傳入code,調(diào)用微信服務器,獲取唯一openid
        // 微信服務器接口地址
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+appid+ "&secret="+appsecret
                +"&js_code="+ code +"&grant_type=authorization_code";
        String errmsg = "";
        String errcode = "";
        String session_key = "";
        String openid = "";
        WeChatSessionModel weChatSessionModel;
        // 發(fā)送請求
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
		// 判斷請求是否成功
        if(responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
			// 獲取主要內(nèi)容
            String sessionData = responseEntity.getBody();
            Gson gson = new Gson();
            //將json字符串轉(zhuǎn)化為實體類;
            weChatSessionModel = gson.fromJson(sessionData, WeChatSessionModel.class);
            log.info("返回的數(shù)據(jù)==>{}",weChatSessionModel);
            //獲取用戶的唯一標識openid
            openid = weChatSessionModel.getOpenid();
            //獲取錯誤碼
            errcode = weChatSessionModel.getErrcode();
            //獲取錯誤信息
            errmsg = weChatSessionModel.getErrmsg();
        }else{
            log.info("出現(xiàn)錯誤,錯誤信息:{}",errmsg );
            map.put("errmsg",errmsg);
            return map;
        }
        // 判斷是否成功獲取到openid
        if ("".equals(openid) || openid == null){
            log.info("錯誤獲取openid,錯誤信息:{}",errmsg);
            map.put("errmsg",errmsg);
            return map;
        }else{
            // 判斷用戶是否存在,查詢數(shù)據(jù)庫
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserInfo::getOpenid, openid);
            UserInfo userInfo = userInfoDao.selectOne(queryWrapper);
            // 不存在,加入數(shù)據(jù)表
            if (userInfo == null){
                // 填充初始信息
                UserInfo tempUserInfo = new UserInfo(UUID.randomUUID().toString(), openid, "微信用戶", 1,"default.png", "",0,0,0);
				// 加入數(shù)據(jù)表
                userInfoDao.insert(tempUserInfo);
				// 加入map返回
                map.put("user",tempUserInfo);
				// 調(diào)用自定義類封裝的方法,創(chuàng)建token
                String token = JWTUtils.createToken(tempUserInfo.getId().toString());
                map.put("token",token);
                return map;
            }else{
                // 存在,將用戶信息加入map返回
                map.put("user",userInfo);
                String token = JWTUtils.createToken(userInfo.getId().toString());
                map.put("token",token);
                return map;
            }
        }
    }
 
 
}

工具類:JWTUtils

 用于創(chuàng)建,驗證和更新token

package com.mh.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
 * Date:2023/5/24
 * author:zmh
 * description:JWT工具類,JWT生成,驗證
 **/
@Component
@Data
@ConfigurationProperties(prefix = "jwt")
@Slf4j
public class JWTUtils {
    //定義token返回頭部
    public static String header;
    //token前綴
    public static String tokenPrefix;
    //簽名密鑰
    public static String secret;
    //有效期
    public static long expireTime;
    public void setHeader(String header) {
        JWTUtils.header = header;
    }
    public void setTokenPrefix(String tokenPrefix) {
        JWTUtils.tokenPrefix = tokenPrefix;
    }
    public void setSecret(String secret) {
        JWTUtils.secret = secret;
    }
    public void setExpireTime(long expireTime) {
        JWTUtils.expireTime = expireTime;
    }
    /**
     * 創(chuàng)建TOKEN
     *
     * @param sub
     * @return
     */
    public static String createToken(String sub) {
        return tokenPrefix + JWT.create()
                .withSubject(sub)
                .withExpiresAt(new Date(System.currentTimeMillis() + expireTime))
                .sign(Algorithm.HMAC512(secret));
    }
    /**
     * 驗證token
     *
     * @param token
     */
    public static String validateToken(String token) {
        try {
            return JWT.require(Algorithm.HMAC512(secret))
                    .build()
                    .verify(token.replace(tokenPrefix, ""))
                    .getSubject();
        } catch (TokenExpiredException e) {
            log.info("token已過期");
            return "";
        } catch (Exception e) {
            log.info("token驗證失敗");
            return "";
        }
    }
    /**
     * 檢查token是否需要更新
     * @param token ·
     * @return
     */
    public static boolean isNeedUpdate(String token) {
        //獲取token過期時間
        Date expiresAt = null;
        try {
            expiresAt = JWT.require(Algorithm.HMAC512(secret))
                    .build()
                    .verify(token.replace(tokenPrefix, ""))
                    .getExpiresAt();
        } catch (TokenExpiredException e) {
            return true;
        } catch (Exception e) {
            log.info("token驗證失敗");
            return false;
        }
        //如果剩余過期時間少于過期時常的一般時 需要更新
        return (expiresAt.getTime() - System.currentTimeMillis()) < (expireTime >> 1);
    }
}

攔截器配置-自定義攔截器

當用戶訪問非登錄接口時,需要攔截請求,判斷用戶的請求頭是否攜帶了正確的token,攜帶了代表登錄過了,請求通過,返回數(shù)據(jù),若未token驗證失敗則錯誤提示。

package com.mh.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * Date:2023/5/26
 * author:zmh
 * description: 自定義登錄攔截器
 **/
@Slf4j
public class UserLoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 獲取請求頭,header值為Authorization,承載token
        String token = request.getHeader(JWTUtils.header);
        //token不存在
        if (token == null || token.equals("")) {
            log.info("傳入token為空");
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token為空!");
            return false;
        }
        //驗證token
        String sub = JWTUtils.validateToken(token);
        if (sub == null || sub.equals("")){
            log.info("token驗證失敗");
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token驗證失敗!");
            return false;
        }
        //更新token有效時間 (如果需要更新其實就是產(chǎn)生一個新的token)
        if (JWTUtils.isNeedUpdate(token)){
            String newToken = JWTUtils.createToken(sub);
            response.setHeader(JWTUtils.header,newToken);
        }
        return true;
    }
}

攔截器配置-注冊自定義攔截器

package com.mh.config;
 
import com.mh.utils.UserLoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
/**
 * Date:2023/5/26
 * author:zmh
 * description: MVW配置
 **/
 
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
 
    /**
     * 注冊自定義攔截器
     * @param registry ·
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserLoginInterceptor())
                .addPathPatterns("/api/**") // 攔截地址
                .excludePathPatterns("/api/userInfo/login");// 開放登錄路徑
    }
 
}

測試(Postman)

1.測試微信一鍵登錄 

微信小程序獲取臨時憑證code

 通過返回的code到postman中測試調(diào)用后端登錄接口

 獲取到返回,代表登錄成功。

2.測試token的驗證 

調(diào)用非登錄接口,會被攔截進行token的檢查。

后端日志輸出: 

 攜帶錯誤或過期的token,驗證失敗

 后端日志輸出 

 攜帶正確且在有效期內(nèi)的token,驗證成功,測試通過。

總結(jié)

 對于如上代碼,其實微信登錄的邏輯是比較簡單的,代碼更多的是在處理身份驗證(token驗證),后端設(shè)置了請求攔截器,會去攔截所有非登錄接口,通過檢查token判斷是否登錄過了。

對于前端發(fā)送請求,如上只是使用了Postman進行接口的訪問,并沒有從代碼層面去發(fā)送請求,那么,其實前端是比較需要去封裝請求方法的,在封裝的請求方法中加入請求頭攜帶token,避免每一次請求都需要手動加上請求頭攜帶token。

到此這篇關(guān)于微信小程序使用uni-app和springboot實現(xiàn)一鍵登錄功能的文章就介紹到這了,更多相關(guān)微信小程序一鍵登錄功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 微信小程序表單驗證form提交錯誤提示效果

    微信小程序表單驗證form提交錯誤提示效果

    這篇文章主要為大家詳細介紹了微信小程序表單驗證form提交錯誤提示效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Atitit.js的鍵盤按鍵事件捆綁and事件調(diào)度

    Atitit.js的鍵盤按鍵事件捆綁and事件調(diào)度

    這篇文章主要介紹了Atitit.js的鍵盤按鍵事件捆綁and事件調(diào)度的相關(guān)資料,需要的朋友可以參考下
    2016-04-04
  • Electron 隱藏頂部菜單功能實現(xiàn)

    Electron 隱藏頂部菜單功能實現(xiàn)

    本文介紹了如何在Electron應用中隱藏頂部菜單,具體方法是在main.js文件中添加一行代碼,這是一個簡單有效的技巧,適用于需要簡化界面的Electron應用
    2024-09-09
  • js觀察者模式的彈幕案例

    js觀察者模式的彈幕案例

    這篇文章主要為大家詳細介紹了js觀察者模式的彈幕案例,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • window.onresize 多次觸發(fā)的解決方法

    window.onresize 多次觸發(fā)的解決方法

    用了window.onresize但是發(fā)現(xiàn)每次 onresize 后頁面中狀態(tài)總是不對,下面與大家分享下onresize 事件多次觸發(fā)的解決方法
    2013-11-11
  • 通過JavaScript看透彩票背后的隨機算法

    通過JavaScript看透彩票背后的隨機算法

    這篇文章主要介紹了如何通過JavaScript看透彩票背后的隨機算法,真實的彩票系統(tǒng)不是這么開發(fā)出來的,也不具備明面上的規(guī)律,我們應該相信彩票的公正性,盡管其可能不是基于隨機,感興趣的朋友可以參考閱讀下
    2023-06-06
  • 基于Bootstrap table組件實現(xiàn)多層表頭的實例代碼

    基于Bootstrap table組件實現(xiàn)多層表頭的實例代碼

    Bootstrap table還有一個很多強大的功能,下面就通過本文給大家分享基于Bootstrap table組件實現(xiàn)多層表頭的實例代碼,需要的朋友參考下吧
    2017-09-09
  • 小程序云開發(fā)如何實現(xiàn)圖片上傳及發(fā)表文字

    小程序云開發(fā)如何實現(xiàn)圖片上傳及發(fā)表文字

    這篇文章主要為大家詳細介紹了小程序云開發(fā)教程,如何實現(xiàn)圖片上傳及發(fā)表文字,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • javascript實現(xiàn)的圖片預覽和上傳功能示例【兼容IE 9】

    javascript實現(xiàn)的圖片預覽和上傳功能示例【兼容IE 9】

    這篇文章主要介紹了javascript實現(xiàn)的圖片預覽和上傳功能,結(jié)合實例形式分析了javascrpt圖片預覽和上傳功能相關(guān)實現(xiàn)技巧與操作注意事項,需要的朋友可以參考下
    2020-05-05
  • Bootstrap3多級下拉菜單

    Bootstrap3多級下拉菜單

    這篇文章主要為大家詳細介紹了Bootstrap3多級下拉菜單的相關(guān)資料,需引用bootstrap.min.css和bootstrap.min.css.js,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02

最新評論

墨竹工卡县| 汕头市| 靖宇县| 合山市| 广西| 鹤壁市| 南宁市| 顺昌县| 章丘市| 奉节县| 泗水县| 长兴县| 博湖县| 宜良县| 南部县| 资兴市| 霞浦县| 敦煌市| 双城市| 沙湾县| 赫章县| 沧源| 咸宁市| 南京市| 高淳县| 龙门县| 灵川县| 齐齐哈尔市| 西安市| 多伦县| 阜新市| 两当县| 汉沽区| 淄博市| 阿荣旗| 九龙坡区| 延川县| 石河子市| 衡南县| 商都县| 县级市|