小程序微信用户登录实现
小程序登录和jwt,httpclient工具类详细介绍可以看下小锋老师的 小程序电商系统课程:https://www.bilibili.com/video/BV1kP4y1F7tU
application.yml加上小程序登录需要的参数,小伙伴们可以登录小程序后台管理,获取到自己的参数
weixin:jscode2sessionUrl: https://api.weixin.qq.com/sns/jscode2sessionappid: 改成你的secret: 改成你的
定义Properties属性类来映射上面的配置
package com.java1234.properties;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** 微信小程序配置文件* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2022-01-08 17:56*/
@Component
@ConfigurationProperties(prefix = "weixin")
@Data
public class WeixinProperties {private String jscode2sessionUrl; // 登录凭证校验请求地址private String appid; // 小程序 appIdprivate String secret; // 小程序 appSecret}
/*** 微信用户登录* @return*/
@RequestMapping("/wxlogin")
public R wxLogin(@RequestBody WxUserInfo wxUserInfo){String jscode2sessionUrl=weixinProperties.getJscode2sessionUrl()+"?appid="+weixinProperties.getAppid()+"&secret="+weixinProperties.getSecret()+"&js_code="+wxUserInfo.getCode()+"&grant_type=authorization_code";System.out.println(jscode2sessionUrl);String result = httpClientUtil.sendHttpGet(jscode2sessionUrl);System.out.println(result);JSONObject jsonObject= JSON.parseObject(result);String openid = jsonObject.get("openid").toString();System.out.println(openid);// 插入用户到数据库 假如说 用户不存在 我们插入用户 如果用户存在 我们更新用户WxUserInfo resultWxUserInfo = wxUserInfoService.getOne(new QueryWrapper<WxUserInfo>().eq("openid", openid));if(resultWxUserInfo==null){System.out.println("不存在 插入用户");wxUserInfo.setOpenid(openid);wxUserInfo.setRegisterDate(new Date());wxUserInfo.setLastLoginDate(new Date());wxUserInfoService.save(wxUserInfo);}else{System.out.println("存在 更新用户");resultWxUserInfo.setLastLoginDate(new Date());wxUserInfoService.updateById(resultWxUserInfo);}if(resultWxUserInfo!=null && resultWxUserInfo.getStatus().equals("1")){ // 被禁用return R.error(400,"用户被禁用,具体请联系管理员!");}else{// 利用jwt生成token返回到前端String token = JwtUtils.createJWT(openid, wxUserInfo.getNickName(), JwtConstant.JWT_TTL);Map<String,Object> resultMap=new HashMap<>();resultMap.put("token",token);resultMap.put("openid",openid);return R.ok(resultMap);}
}
设置appId
util
package com.java1234.util;import java.text.SimpleDateFormat;
import java.util.Date;/*** 日期工具类* @author Administrator**/
public class DateUtil {/*** 日期对象转字符串* @param date* @param format* @return*/public static String formatDate(Date date,String format){String result="";SimpleDateFormat sdf=new SimpleDateFormat(format);if(date!=null){result=sdf.format(date);}return result;}/*** 字符串转日期对象* @param str* @param format* @return* @throws Exception*/public static Date formatString(String str,String format) throws Exception{if(StringUtil.isEmpty(str)){return null;}SimpleDateFormat sdf=new SimpleDateFormat(format);return sdf.parse(str);}public static String getCurrentDateStr(){Date date=new Date();SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddhhmmssSSSSSSSSS");return sdf.format(date);}public static String getCurrentDatePath()throws Exception{Date date=new Date();SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd/");return sdf.format(date);}public static void main(String[] args) {try {System.out.println(getCurrentDateStr());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
package com.java1234.util;import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.util.PublicSuffixMatcher;
import org.apache.http.conn.util.PublicSuffixMatcherLoader;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** httpClient 工具类* @author java1234_小锋* @site www.java1234.com* @company Java知识分享网* @create 2019-02-10 下午 2:49*/
@Component
public class HttpClientUtil {/*** 默认参数设置* setConnectTimeout:设置连接超时时间,单位毫秒。* setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。* setSocketTimeout:请求获取数据的超时时间,单位毫秒。访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。 暂时定义15分钟*/private RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(600000).setConnectTimeout(600000).setConnectionRequestTimeout(600000).build();/*** 静态内部类---作用:单例产生类的实例* @author Administrator**/private static class LazyHolder { private static final HttpClientUtil INSTANCE = new HttpClientUtil(); } private HttpClientUtil(){}public static HttpClientUtil getInstance(){return LazyHolder.INSTANCE; }/*** 发送 post请求* @param httpUrl 地址*/public String sendHttpPost(String httpUrl) {HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost return sendHttpPost(httpPost);}/*** 发送 post请求* @param httpUrl 地址* @param params 参数(格式:key1=value1&key2=value2)*/public String sendHttpPost(String httpUrl, String params) {HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost try {//设置参数StringEntity stringEntity = new StringEntity(params, "UTF-8");stringEntity.setContentType("application/x-www-form-urlencoded");httpPost.setEntity(stringEntity);} catch (Exception e) {e.printStackTrace();}return sendHttpPost(httpPost);}/*** 发送 post请求* @param httpUrl 地址* @param maps 参数*/public String sendHttpPost(String httpUrl, Map<String, String> maps) {HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost // 创建参数队列 List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();for (String key : maps.keySet()) {nameValuePairs.add(new BasicNameValuePair(key, maps.get(key)));}try {httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));} catch (Exception e) {e.printStackTrace();}return sendHttpPost(httpPost);}/*** 发送Post请求* @param httpPost* @return*/private String sendHttpPost(HttpPost httpPost) {CloseableHttpClient httpClient = null;CloseableHttpResponse response = null;HttpEntity entity = null;String responseContent = null;try {// 创建默认的httpClient实例httpClient = HttpClients.createDefault();httpPost.setConfig(requestConfig);// 执行请求long execStart = System.currentTimeMillis();response = httpClient.execute(httpPost);long execEnd = System.currentTimeMillis();System.out.println("=================执行post请求耗时:"+(execEnd-execStart)+"ms");long getStart = System.currentTimeMillis();entity = response.getEntity();responseContent = EntityUtils.toString(entity, "UTF-8");long getEnd = System.currentTimeMillis();System.out.println("=================获取响应结果耗时:"+(getEnd-getStart)+"ms");} catch (Exception e) {e.printStackTrace();} finally {try {// 关闭连接,释放资源if (response != null) {response.close();}if (httpClient != null) {httpClient.close();}} catch (IOException e) {e.printStackTrace();}}return responseContent;}/*** 发送 get请求* @param httpUrl*/public String sendHttpGet(String httpUrl) {HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求return sendHttpGet(httpGet);}/*** 发送 get请求Https* @param httpUrl*/public String sendHttpsGet(String httpUrl) {HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求return sendHttpsGet(httpGet);}/*** 发送Get请求* @param httpGet* @return*/private String sendHttpGet(HttpGet httpGet) {CloseableHttpClient httpClient = null;CloseableHttpResponse response = null;HttpEntity entity = null;String responseContent = null;try {// 创建默认的httpClient实例.httpClient = HttpClients.createDefault();httpGet.setConfig(requestConfig);// 执行请求response = httpClient.execute(httpGet);entity = response.getEntity();responseContent = EntityUtils.toString(entity, "UTF-8");} catch (Exception e) {e.printStackTrace();} finally {try {// 关闭连接,释放资源if (response != null) {response.close();}if (httpClient != null) {httpClient.close();}} catch (IOException e) {e.printStackTrace();}}return responseContent;}/*** 发送Get请求Https* @param httpGet* @return*/private String sendHttpsGet(HttpGet httpGet) {CloseableHttpClient httpClient = null;CloseableHttpResponse response = null;HttpEntity entity = null;String responseContent = null;try {// 创建默认的httpClient实例.PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(new URL(httpGet.getURI().toString()));DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);httpClient = HttpClients.custom().setSSLHostnameVerifier(hostnameVerifier).build();httpGet.setConfig(requestConfig);// 执行请求response = httpClient.execute(httpGet);entity = response.getEntity();responseContent = EntityUtils.toString(entity, "UTF-8");} catch (Exception e) {e.printStackTrace();} finally {try {// 关闭连接,释放资源if (response != null) {response.close();}if (httpClient != null) {httpClient.close();}} catch (IOException e) {e.printStackTrace();}}return responseContent;}/*** 发送xml数据* @param url* @param xmlData* @return* @throws ClientProtocolException* @throws IOException*/public static HttpResponse sendXMLDataByPost(String url, String xmlData)throws ClientProtocolException, IOException {HttpClient httpClient = HttpClients.createDefault();HttpPost httppost = new HttpPost(url);StringEntity entity = new StringEntity(xmlData);httppost.setEntity(entity);httppost.setHeader("Content-Type", "text/xml;charset=UTF-8");HttpResponse response = httpClient.execute(httppost);return response;}/*** 获得响应HTTP实体内容** @param response* @return* @throws IOException* @throws UnsupportedEncodingException*/public static String getHttpEntityContent(HttpResponse response) throws IOException, UnsupportedEncodingException {HttpEntity entity = response.getEntity();if (entity != null) {InputStream is = entity.getContent();BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));String line = br.readLine();StringBuilder sb = new StringBuilder();while (line != null) {sb.append(line + "\n");line = br.readLine();}return sb.toString();}return "";}}
package com.java1234.util;import com.java1234.constant.JwtConstant;
import com.java1234.entity.CheckResult;
import io.jsonwebtoken.*;
import org.bouncycastle.util.encoders.Base64;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;/*** jwt加密和解密的工具类* @author java1234_小锋* @site www.java1234.com* @company Java知识分享网* @create 2019-08-13 上午 10:06*/
public class JwtUtils {/*** 签发JWT* @param id* @param subject 可以是JSON数据 尽可能少* @param ttlMillis* @return*/public static String createJWT(String id, String subject, long ttlMillis) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);SecretKey secretKey = generalKey();JwtBuilder builder = Jwts.builder().setId(id).setSubject(subject) // 主题.setIssuer("Java1234") // 签发者.setIssuedAt(now) // 签发时间.signWith(signatureAlgorithm, secretKey); // 签名算法以及密匙if (ttlMillis >= 0) {long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);builder.setExpiration(expDate); // 过期时间}return builder.compact();}/*** 生成jwt token* @param username* @return*/public static String genJwtToken(String username){return createJWT(username,username,60*60*1000);}/*** 验证JWT* @param jwtStr* @return*/public static CheckResult validateJWT(String jwtStr) {CheckResult checkResult = new CheckResult();Claims claims = null;try {claims = parseJWT(jwtStr);checkResult.setSuccess(true);checkResult.setClaims(claims);} catch (ExpiredJwtException e) {checkResult.setErrCode(JwtConstant.JWT_ERRCODE_EXPIRE);checkResult.setSuccess(false);} catch (SignatureException e) {checkResult.setErrCode(JwtConstant.JWT_ERRCODE_FAIL);checkResult.setSuccess(false);} catch (Exception e) {checkResult.setErrCode(JwtConstant.JWT_ERRCODE_FAIL);checkResult.setSuccess(false);}return checkResult;}/*** 生成加密Key* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.decode(JwtConstant.JWT_SECERT);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 解析JWT字符串* @param jwt* @return* @throws Exception*/public static Claims parseJWT(String jwt) {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}public static void main(String[] args) throws InterruptedException {//小明失效 10sString sc = createJWT("1","小明", 60 * 60 * 1000);System.out.println(sc);System.out.println(validateJWT(sc).getErrCode());System.out.println(validateJWT(sc).getClaims().getId());System.out.println(validateJWT(sc).getClaims().getSubject());//Thread.sleep(3000);System.out.println(validateJWT(sc).getClaims());Claims claims = validateJWT(sc).getClaims();String sc2 = createJWT(claims.getId(),claims.getSubject(), JwtConstant.JWT_TTL);System.out.println(sc2);}}
package com.java1234.util;import java.util.ArrayList;
import java.util.List;
import java.util.Random;/*** 字符串工具类* @author **/
public class StringUtil {/*** 判断是否是空* @param str* @return*/public static boolean isEmpty(String str){if(str==null||"".equals(str.trim())){return true;}else{return false;}}/*** 判断是否不是空* @param str* @return*/public static boolean isNotEmpty(String str){if((str!=null)&&!"".equals(str.trim())){return true;}else{return false;}}/*** 格式化模糊查询* @param str* @return*/public static String formatLike(String str){if(isNotEmpty(str)){return "%"+str+"%";}else{return null;}}/*** 过滤掉集合里的空格* @param list* @return*/public static List<String> filterWhite(List<String> list){List<String> resultList=new ArrayList<String>();for(String l:list){if(isNotEmpty(l)){resultList.add(l);}}return resultList;}/*** 去除html标签*/public static String stripHtml(String content) { // <p>段落替换为换行 content = content.replaceAll("<p .*?>", "\r\n"); // <br><br/>替换为换行 content = content.replaceAll("<br\\s*/?>", "\r\n"); // 去掉其它的<>之间的东西 content = content.replaceAll("\\<.*?>", ""); // 去掉空格 content = content.replaceAll(" ", ""); return content; }/*** 生成六位随机数* @return*/public static String genSixRandomNum(){Random random = new Random();String result="";for (int i=0;i<6;i++){result+=random.nextInt(10);}return result;}/*** 生成由[A-Z,0-9]生成的随机字符串* @param length 欲生成的字符串长度* @return*/public static String getRandomString(int length){Random random = new Random();StringBuffer sb = new StringBuffer();for(int i = 0; i < length; ++i){int number = random.nextInt(2);long result = 0;switch(number){case 0:result = Math.round(Math.random() * 25 + 65);sb.append(String.valueOf((char)result));break;case 1:sb.append(String.valueOf(new Random().nextInt(10)));break;}}return sb.toString();}}
constant
package com.java1234.constant;/*** 系统级静态变量* @author java1234_小锋* @site www.java1234.com* @company Java知识分享网* @create 2019-08-13 上午 9:51*/
public class JwtConstant {/*** token*/public static final int JWT_ERRCODE_NULL = 4000; //Token不存在public static final int JWT_ERRCODE_EXPIRE = 4001; //Token过期public static final int JWT_ERRCODE_FAIL = 4002; //验证不通过/*** JWT*/public static final String JWT_SECERT = "8677df7fc3a34e26a61c034d5ec8245d"; //密匙public static final long JWT_TTL = 24*60 * 60 * 1000; //token有效时间
}
R
package com.java1234.entity;import java.util.HashMap;
import java.util.Map;/*** 页面响应entity* @author java1234_小锋* @site www.java1234.com* @company Java知识分享网* @create 2019-08-13 上午 10:00*/
public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public R() {put("code", 0);}public static R error() {return error(500, "未知异常,请联系管理员");}public static R error(String msg) {return error(500, msg);}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);return r;}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}public R put(String key, Object value) {super.put(key, value);return this;}
}
package com.java1234.entity;import io.jsonwebtoken.Claims;/*** jwt验证信息* @author java1234_小锋* @site www.java1234.com* @company Java知识分享网* @create 2019-08-13 上午 10:00*/
public class CheckResult {private int errCode;private boolean success;private Claims claims;public int getErrCode() {return errCode;}public void setErrCode(int errCode) {this.errCode = errCode;}public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public Claims getClaims() {return claims;}public void setClaims(Claims claims) {this.claims = claims;}}
requestUtils封装:
// 同时发送异步代码的次数
let ajaxTimes=0;// 定义公共的url
const baseUrl="http://localhost:8866";/*** 返回baseUrl*/
export const getBaseUrl=()=>{return baseUrl;
}/*** 后端请求工具类* @param {*} params 请求参数*/
export const requestUtil=(params)=>{let header={...params.header};// 拼接header 带上tokenheader["token"]=uni.getStorageSync("token");ajaxTimes++;// 显示加载中 效果wx.showLoading({title: "加载中",mask: true});var start = new Date().getTime();// 模拟网络延迟加载while(true) if(new Date().getTime()-start > 1000*1) break;return new Promise((resolve,reject)=>{wx.request({...params,header:header,url:baseUrl+params.url,success:(result)=>{resolve(result.data);},fail:(err)=>{uni.showToast({icon:'error',title:'连接服务器失败',duration:3000})reject(err);},complete:()=>{ajaxTimes--;if(ajaxTimes===0){// 关闭正在等待的图标wx.hideLoading();}}});})
}
App.vue
<script>import {requestUtil} from "./utils/requestUtil.js"export default {onLaunch: function() {console.log('App Launch')wx.login({timeout: 5000,success:(res)=>{this.wxlogin(res.code);}});},onShow: function() {console.log('App Show')},onHide: function() {console.log('App Hide')},methods:{/*** 请求后端获取用户token* @param {} code */async wxlogin(code){console.log("code="+code)// 发送请求 获取用户的tokenconst result=await requestUtil({url:"/user/wxlogin",data:{code:code},method:"post"});console.log("token="+result.token);console.log("openid="+result.openid);if(result.code==0){console.log("登录成功")uni.setStorageSync("token",result.token);uni.setStorageSync("openid",result.openid);}else{console.log("登录失败,报错信息:"+result.msg);uni.showToast({icon:'error',title:result.msg,duration:3000})}}}}
</script><style>/*每个页面公共css */body,page{background-color: #f4f5f7;}
</style>
//设置默认值为0
//设置默认值为0private String status="0"; // 用户状态 状态 0 可用 1 封禁
package com.java1234.entity;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 微信用户信息实体* @author java1234_小锋* @site www.java1234.com* @company 南通小锋网络科技有限公司* @create 2022-01-08 15:59*/
@TableName("t_wxUserInfo")
@Data
public class WxUserInfo implements Serializable {private Integer id; // 用户编号private String openid; // 用户唯一标识private String nickName="微信用户"; // 用户昵称private String avatarUrl="default.png"; // 用户头像图片的 URL@JsonSerialize(using=CustomDateTimeSerializer.class)private Date registerDate; // 注册日期@JsonSerialize(using=CustomDateTimeSerializer.class)private Date lastLoginDate; // 最后登录日期//设置默认值为0private String status="0"; // 用户状态 状态 0 可用 1 封禁@TableField(select = false,exist = false)private String code; // 微信用户code 前端传来的}