SpringBoot后端實現(xiàn)小程序微信登錄功能實現(xiàn)
SpringBoot實現(xiàn)微信小程序登錄簡介
微信小程序登錄是開發(fā)者通過微信提供的身份驗證機(jī)制,獲取用戶唯一標(biāo)識(openid)和會話密鑰(session_key)的過程。SpringBoot作為后端框架,可以與小程序前端配合完成完整的登錄流程。
小程序端調(diào)用wx.login()
小程序前端調(diào)用此API獲取臨時登錄憑證code
示例代碼:
wx.login({ success(res) { if (res.code) { // 發(fā)送code到后端 } } })
SpringBoot后端處理登錄
接收小程序傳來的code
向微信接口服務(wù)發(fā)起請求驗證code
獲取用戶唯一標(biāo)識openid和會話密鑰session_key
返回自定義登錄態(tài)
后端生成自定義登錄態(tài)(如token)并返回給小程序
小程序后續(xù)請求攜帶此登錄態(tài)
SpringBoot后端實現(xiàn)微信登錄
1.導(dǎo)入HttpClient的Maven坐標(biāo)
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency>
2.Http工具類
/** * Http工具類 */ public class HttpClientUtil { ? static final int TIMEOUT_MSEC = 5 * 1000; ? /** * 發(fā)送GET方式請求 * @param url * @param paramMap * @return */ public static String doGet(String url,Map<String,String> paramMap){ // 創(chuàng)建Httpclient對象 CloseableHttpClient httpClient = HttpClients.createDefault(); ? String result = ""; CloseableHttpResponse response = null; ? try{ URIBuilder builder = new URIBuilder(url); if(paramMap != null){ for (String key : paramMap.keySet()) { builder.addParameter(key,paramMap.get(key)); } } URI uri = builder.build(); ? //創(chuàng)建GET請求 HttpGet httpGet = new HttpGet(uri); ? //發(fā)送請求 response = httpClient.execute(httpGet); ? //判斷響應(yīng)狀態(tài) if(response.getStatusLine().getStatusCode() == 200){ result = EntityUtils.toString(response.getEntity(),"UTF-8"); } }catch (Exception e){ e.printStackTrace(); }finally { try { response.close(); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } ? return result; } ? /** * 發(fā)送POST方式請求 * @param url * @param paramMap * @return * @throws IOException */ public static String doPost(String url, Map<String, String> paramMap) throws IOException { // 創(chuàng)建Httpclient對象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; ? try { // 創(chuàng)建Http Post請求 HttpPost httpPost = new HttpPost(url); ? // 創(chuàng)建參數(shù)列表 if (paramMap != null) { List<NameValuePair> paramList = new ArrayList(); for (Map.Entry<String, String> param : paramMap.entrySet()) { paramList.add(new BasicNameValuePair(param.getKey(), param.getValue())); } // 模擬表單 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList); httpPost.setEntity(entity); } ? httpPost.setConfig(builderRequestConfig()); ? // 執(zhí)行http請求 response = httpClient.execute(httpPost); ? resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } catch (Exception e) { throw e; } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } ? return resultString; } ? /** * 發(fā)送POST方式請求 * @param url * @param paramMap * @return * @throws IOException */ public static String doPost4Json(String url, Map<String, String> paramMap) throws IOException { // 創(chuàng)建Httpclient對象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; ? try { // 創(chuàng)建Http Post請求 HttpPost httpPost = new HttpPost(url); ? if (paramMap != null) { //構(gòu)造json格式數(shù)據(jù) JSONObject jsonObject = new JSONObject(); for (Map.Entry<String, String> param : paramMap.entrySet()) { jsonObject.put(param.getKey(),param.getValue()); } StringEntity entity = new StringEntity(jsonObject.toString(),"utf-8"); //設(shè)置請求編碼 entity.setContentEncoding("utf-8"); //設(shè)置數(shù)據(jù)類型 entity.setContentType("application/json"); httpPost.setEntity(entity); } ? httpPost.setConfig(builderRequestConfig()); ? // 執(zhí)行http請求 response = httpClient.execute(httpPost); ? resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } catch (Exception e) { throw e; } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } ? return resultString; } private static RequestConfig builderRequestConfig() { return RequestConfig.custom() .setConnectTimeout(TIMEOUT_MSEC) .setConnectionRequestTimeout(TIMEOUT_MSEC) .setSocketTimeout(TIMEOUT_MSEC).build(); } ? }
3.JWT工具類
import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.Map; ? public class JwtUtil { /** * 生成jwt * 使用Hs256算法, 私匙使用固定秘鑰 * * @param secretKey jwt秘鑰 * @param ttlMillis jwt過期時間(毫秒) * @param claims 設(shè)置的信息 * @return */ public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) { // 指定簽名的時候使用的簽名算法,也就是header那部分 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; ? // 生成JWT的時間 long expMillis = System.currentTimeMillis() + ttlMillis; Date exp = new Date(expMillis); ? // 設(shè)置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有聲明,一定要先設(shè)置這個自己創(chuàng)建的私有的聲明,這個是給builder的claim賦值,一旦寫在標(biāo)準(zhǔn)的聲明賦值之后,就是覆蓋了那些標(biāo)準(zhǔn)的聲明的 .setClaims(claims) // 設(shè)置簽名使用的簽名算法和簽名使用的秘鑰 .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8)) // 設(shè)置過期時間 .setExpiration(exp); ? return builder.compact(); } ? /** * Token解密 * * @param secretKey jwt秘鑰 此秘鑰一定要保留好在服務(wù)端, 不能暴露出去, 否則sign就可以被偽造, 如果對接多個客戶端建議改造成多個 * @param token 加密后的token * @return */ public static Claims parseJWT(String secretKey, String token) { // 得到DefaultJwtParser Claims claims = Jwts.parser() // 設(shè)置簽名的秘鑰 .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)) // 設(shè)置需要解析的jwt .parseClaimsJws(token).getBody(); return claims; } ? }
4.準(zhǔn)備好簽名所需使用的密鑰,過期時間等
sky: jwt: user-secret-key: itcast # 設(shè)置jwt過期時間 user-ttl: 7200000 # 設(shè)置前端傳遞過來的令牌名稱 user-token-name: authentication
5.用對象類將配置文件的屬性封裝
@Component @ConfigurationProperties(prefix = "sky.jwt") @Data public class JwtProperties { /** * 用戶端微信用戶生成jwt令牌相關(guān)配置 */ private String userSecretKey; private long userTtl; private String userTokenName; ? }
6.WeChatProperties對象類部分屬性以及配置文件
關(guān)于怎么注冊大家可以移步最下方。
@Component @ConfigurationProperties(prefix = "sky.wechat") @Data public class WeChatProperties { ? private String appid; //小程序的appid private String secret; //小程序的秘鑰 ? } ? //application.yml配置文件 sky: wechat: appid: ${sky.wechat.appid} secret: ${sky.wechat.secret} ? //application-dev.yml配置文件 sky: wechat: # TODO:需要在微信公眾平臺申請相關(guān)信息并填入 appid: ****** secret: ******
7.準(zhǔn)備好DTO數(shù)據(jù)對象
/** * C端用戶登錄 */ @Data public class UserLoginDTO implements Serializable { ? private String code; ? }
8.控制層接口
@RestController @RequestMapping("/user/user") @Slf4j @RequiredArgsConstructor // Lombok 自動生成構(gòu)造函數(shù) @Api(tags = "C端用戶相關(guān)接口") public class UserController { private final UserService userService; private final JwtProperties jwtProperties; /** * 微信登錄 * @param userLoginDTO * @return */ @PostMapping("/login") @ApiOperation("微信登錄") public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){ log.info("微信用戶登錄:{}",userLoginDTO.getCode()); ? //微信登錄 User user = userService.wxLogin(userLoginDTO); ? //為微信用戶生成jwt令牌 Map<String,Object> claims = new HashMap<>(); 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(); ? return Result.success(userLoginVO); } }
9.微信登錄的服務(wù)層的接口和實現(xiàn)類
//接口 public interface UserService { /** * 微信登錄接口 * @param userLoginDTO * @return */ User wxLogin(UserLoginDTO userLoginDTO); } ? //實現(xiàn)類 @Service @Slf4j public class UserServiceImpl implements UserService { public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session"; @Autowired private WeChatProperties weChatProperties; @Autowired private UserMapper userMapper; ? /** * 微信登錄 * @param userLoginDTO * @return */ @Override public User wxLogin(UserLoginDTO userLoginDTO) { String openid = getOpenid(userLoginDTO.getCode()); //判斷openid是否為空,如果為空表示登錄失敗,拋出業(yè)務(wù)異常 if(openid == null){ throw new LoginFailedException(MessageConstant.LOGIN_FAILED); } ? //判斷當(dāng)前用戶是否是新用戶 User user = userMapper.getByOpenid(openid); ? //如果是新用戶,自動完成注冊 if(user == null){ user = User.builder() .openid(openid) .createTime(LocalDateTime.now()) .build(); userMapper.insert(user); } //返回這個用戶對象 ? return user; } ? /** * 調(diào)用微信接口服務(wù),獲取當(dāng)前微信用戶的openid * @param code * @return */ private String getOpenid(String code){ Map<String, String> map = new HashMap<>(); 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; } } ?
注冊小程序,獲取key
微信公眾平臺
到此這篇關(guān)于SpringBoot后端實現(xiàn)小程序微信登錄的文章就介紹到這了,更多相關(guān)SpringBoot小程序微信登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 如何把byte轉(zhuǎn)化為KB、MB、GB的方法
這篇文章主要介紹了java 如何把byte轉(zhuǎn)化為KB、MB、GB的方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10