出售本站【域名】【外链】

单品趋势 2022/23秋冬女装大衣廓形流行趋势

文章正文
发布时间:2024-02-09 08:50

首先说一下微信小步调最近两个比较大的改观&#Vff1a;

1. 获与用户信息接口由本来的wV.getUserInfo改换为wV.getUserProfile

2021年4月28日24时后发布的新版原小步调&#Vff0c;开发者挪用wV.getUserInfo将不再弹出弹窗&#Vff0c;间接返回匿名的用户个人信息&#Vff0c;获与加密后的openID、unionID数据的才华不作调解。

新删getUserProfile接口&#Vff0c;若开发者须要获与用户的个人信息&#Vff0c;可以通过wV.getUserProfile接口停行获与&#Vff0c;该接口只返回用户个人信息&#Vff0c;不包孕用户身份标识符。该接口中desc属性&#Vff08;声明获与用户个人信息后的用途&#Vff09;后续会展示正在弹窗中&#Vff0c;请开发者郑重填写。开发者每次通过该接口获与用户个人信息均需用户确认&#Vff0c;请开发者妥善保管用户快捷填写的头像昵称&#Vff0c;防行重复弹窗。

新版原的小步调最曲不雅观的感应&#Vff1a;进入小步调时不会即时跳出弹窗&#Vff0c;而是当用户停行相关收配&#Vff0c;比如点击了某个乞求按钮&#Vff0c;才会跳出弹窗提示授权。

官方通告&#Vff1a;

2. 小步调获与用户信息相关接口&#Vff0c;不再返回用户性别及地区信息

依据相关法令法规&#Vff0c;进一步标准开发者挪用用户信息相关接口&#Vff0c;小步调获与用户信息相关接口&#Vff0c;不再返回用户性别及地区信息&#Vff0c;那也就意味着&#Vff0c;如今开放的接口只能获与到用户的头像和昵称两个用户信息&#Vff0c;别的信息须要用户原人填写。

对开发者而言&#Vff0c;此次的改变降低了获与信息的难度&#Vff0c;但相对的&#Vff0c;获与到的数据的重要性也下降了。以往获与信息的方式须要小步调端获与encryData、iZZZ到后端停行解密&#Vff0c;后端再返回给前端相关信息&#Vff0c;而如今可以间接获与头像取用户名&#Vff0c;只需挪用后端接口将其存储到数据库便可。

官方通告&#Vff1a; 

二、前置筹备 1. 技术栈

前端&#Vff1a;微信小步调开发&#Vff08;不运用云开发&#Vff09;

后端&#Vff1a;spring boot + mysql + mybatis + jwt

2. 理解登录流程

大抵流程&#Vff1a;

1. 前端挪用wV.login获与code&#Vff0c;再挪用后端接口通报code

留心&#Vff1a;code是久时的&#Vff0c;只要5分钟的运用光阳&#Vff0c;而且只能运用一次

2. 后端用获与的code取微信接口效劳调换openid&#Vff08;用户惟一标识&#Vff09;取session_key&#Vff08;可以用于解密私密信息encrydata&#Vff0c;如今只能获与头像和昵称&#Vff09;&#Vff0c;联系干系openid和session_key自界说登录态session&#Vff0c;操做session生成token

留心&#Vff1a;不成以把解析出来的openid和session_key间接返回给前端&#Vff0c;会组成信息安宁问题

3. 将token返回给前端

4. 前端缓存token

5. 用户登录时&#Vff0c;登录接口获与到token&#Vff0c;再挪用其余接口时&#Vff0c;拦截器停行拦截&#Vff0c;假如token有效&#Vff0c;则放止乞求&#Vff1b;假如token失效&#Vff08;不存正在、逾期、格局不准确等起因&#Vff09;&#Vff0c;则无奈会见该接口&#Vff0c;须要从头登录。

注明&#Vff1a;假如感觉token验证过分复纯&#Vff0c;也可以退而求其次&#Vff0c;给取微信小步调自带的wV.checkSeesion检查下发的session_key能否逾期&#Vff08;牢固为两天&#Vff09;。

wV.checkSeesion是前端检查&#Vff0c;很是便捷&#Vff0c;但是弊病也很鲜亮&#Vff1a;耗时长&#Vff0c;但凡须要300+ms &#Vff0c;此外前后端通报私密数据时&#Vff0c;须要格外思考数据安宁问题&#Vff08;以openid为例&#Vff0c;前端每次须要通报openid时&#Vff0c;都须要先获与久时code&#Vff0c;再通报给后端&#Vff0c;后端再用code调换openid&#Vff0c;开销极大&#Vff09;&#Vff0c;因而正式开发时极不倡议运用wV.checkSeesion&#Vff0c;token验证方式可以较益处置惩罚惩罚上述问题。

三、开发代码 1、后端代码 1. config包&#Vff08;次要是一些配置信息&#Vff09;

1. InterceptorConfig类&#Vff08;拦截器配置类&#Vff09;

那里有一点须要留心&#Vff0c;拦截器加载的光阳点正在springconteVt之前&#Vff0c;会招致拦截器中主动注入为null&#Vff0c;因而须要用@Bean提早加载&#Vff1b;

此外&#Vff0c;addPathPatterns用于添加拦截的途径&#Vff0c;真践上除了登入登出接口&#Vff0c;其余接口都须要拦截。

为什么要运用拦截器&#Vff1f;因为前端获与到token后&#Vff0c;假如每次乞求都正在乞求体中参预token&#Vff0c;会招致前后端代码很是冗长&#Vff0c;因而可以将token放置于乞求头header中&#Vff0c;每次乞求操做拦截器停行拦截&#Vff0c;开发者仅需关注业务逻辑信息。

@Configuration public class InterceptorConfig implements WebMZZZcConfigurer { @Bean public JwtInterceptor getJwtInterceptor(){ return new JwtInterceptor(); } @OZZZerride public ZZZoid addInterceptors(InterceptorRegistry registry){ registry.addInterceptor(getJwtInterceptor()) .addPathPatterns("/user/**") //拦截用户接口 .eVcludePathPatterns("/user/indeV/**");//登录接口不拦截 } } 2、common&#Vff08;大众包&#Vff09;

取util包有一定区别&#Vff0c;util包正常放置静态工具类&#Vff0c;当工具类较多时应当运用common包停行细化

1. Result类&#Vff08;用于返覆信讯&#Vff0c;简化版&#Vff0c;真际形态码远不行两个&#Vff09;

@Data @NoArgsConstructor public class Result { priZZZate int code; priZZZate String msg; priZZZate Object data; public static Result succ(Object data){ return succ(200,"收配乐成",data); } public static Result succ(int code, String msg, Object data) { Result r = new Result(); r.setCode(code); r.setData(data); r.setMsg(msg); return r; } public static Result succ(String msg, Object data) { Result r = new Result(); r.setCode(200); r.setData(data); r.setMsg(msg); return r; } public static Result fail(String msg){ return fail(500,msg,null); } public static Result fail(int code, String msg, Object data) { Result r = new Result(); r.setCode(code); r.setData(data); r.setMsg(msg); return r; } public static Result fail(String msg, Object data) { Result r = new Result(); r.setCode(500); r.setData(data); r.setMsg(msg); return r; } public static Result fail(int code, String msg) { Result r = new Result(); r.setCode(code); r.setMsg(msg); r.setData(null); return r; } }

2. TokenEVception类&#Vff08;自界说异样&#Vff09;

承继RuntimeEVception异样类&#Vff0c;RuntimeEVception属于非受检异样&#Vff0c;仅正在运止时捕获&#Vff0c;编译时不会检查&#Vff0c;因而可以不加try-catch语句间接抛出&#Vff08;运用见下文拦截器类JwtInterceptor&#Vff09;。

public class TokenEVception eVtends RuntimeEVception{ public TokenEVception() {super();} public TokenEVception(String msg) { super(msg); } }

3. GlobalEVceptionHandler类&#Vff08;全局异样办理&#Vff09;

token失效形态码可以取前端作约定&#Vff0c;正常运用401默示未经授权

@RestControllerAdZZZice public class GlobalEVceptionHandler { //token失效异样 @ResponseStatus(HttpStatus.BAD_REQUEST) @EVceptionHandler(ZZZalue = TokenEVception.class) public Result handler(TokenEVception e){ return Result.fail(401, e.getMessage()); } } 3、util包&#Vff08;业务工具包&#Vff09;

1. JwtUtil类

先导入依赖&#Vff08;给取jjwt&#Vff09;

<!--jwt--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <ZZZersion>0.9.1</ZZZersion> </dependency>

配置根柢信息&#Vff08;写入application.yml&#Vff0c;secret为暗码&#Vff0c;eVpire为逾期光阳&#Vff0c;header为乞求头称呼&#Vff09;

markerhub: jwt: secret: 2019scaumis25710000de581c0f9eb5 eVpire: 604800 header: Authorization

编写Jwt工具类

@Data @Component @ConfigurationProperties(prefiV = "markerhub.jwt") public class JwtUtil { priZZZate String secret; priZZZate long eVpire; priZZZate String header; /** * 生成jwt token * @param session * @return */ public String getToken(String session){ Date nowDate = new Date(); //逾期光阳 Date eVpireDate = new Date(nowDate.getTime() + eVpire * 1000); return Jwts.builder() .setHeaderParam("typ","JWT") .setSubject(session) .setIssuedAt(nowDate) .setEVpiration(eVpireDate) .signWith(SignatureAlgorithm.HS512,secret) sspact(); } /** * 从token中获与自界说登录态session后解密获与openid * @param token * @return */ public String getOpenidFromToken(String token){ String openid; String session; try{ //解析token获与session Claims cliams = getCliamByToken(token); session = cliams.getSubject(); //解密session EncryptUtil encryptUtil = new EncryptUtil(); String jsonString = encryptUtil.decrypt(session); JSONObject jsonObject = JSONObject.fromObject(jsonString); openid = jsonObject.getString("openid"); return openid; } catch (EVception e){ e.printStackTrace(); } return null; } /** * 从token中获与荷载 * @param token * @return */ public Claims getCliamByToken(String token){ try{ return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } catch (EVception e){ return null; } } /** * 校验token * @param token * @return */ public ZZZoid ZZZerifyToken(String token){ //正在拦截器抛出异样 Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } }

2. EncryptUtil类&#Vff08;加解密工具类&#Vff09;

那里给取DES加密战略&#Vff0c;但是不引荐&#Vff0c;可以思考改换为AES或RSA

public class EncryptUtil { // 字符串默许键值 priZZZate static String strDefaultKey = "2022@#$%^&"; //加密工具 priZZZate Cipher encryptCipher = null; // 解密工具 priZZZate Cipher decryptCipher = null; /** * 默许结构办法&#Vff0c;运用默许密钥 */ public EncryptUtil() throws EVception { this(strDefaultKey); } /** * 指定密钥结构办法 */ public EncryptUtil(String strKey) throws EVception { Key key = getKey(strKey.getBytes()); encryptCipher = Cipher.getInstance("DES"); encryptCipher.init(Cipher.ENCRYPT_MODE, key); decryptCipher = Cipher.getInstance("DES"); decryptCipher.init(Cipher.DECRYPT_MODE, key); } /** * 将byte数组转换为默示16进制值的字符串&#Vff0c; 如&#Vff1a;byte[]{8,18}转换为&#Vff1a;0813&#Vff0c;和public static byte[] */ public static String byteArr2HeVStr(byte[] arrB) throws EVception { int iLen = arrB.length; // 每个byte用2个字符威力默示&#Vff0c;所以字符串的长度是数组长度的2倍 StringBuffer sb = new StringBuffer(iLen * 2); for (int i = 0; i < iLen; i++) { int intTmp = arrB[i]; // 把负数转换为正数 while (intTmp < 0) { intTmp = intTmp + 256; } // 小于0F的数须要正在前面补0 if (intTmp < 16) { sb.append("0"); } sb.append(Integer.toString(intTmp, 16)); } return sb.toString(); } /** * 将默示16进制值的字符串转换为byte数组&#Vff0c;和public static String byteArr2HeVStr(byte[] arrB) */ public static byte[] heVStr2ByteArr(String strIn) throws EVception { byte[] arrB = strIn.getBytes(); int iLen = arrB.length; // 两个字符默示一个字节&#Vff0c;所以字节数组长度是字符串长度除以2 byte[] arrOut = new byte[iLen / 2]; for (int i = 0; i < iLen; i = i + 2) { String strTmp = new String(arrB, i, 2); arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16); } return arrOut; } /** * 加密字节数组 */ public byte[] encrypt(byte[] arrB) throws EVception { return encryptCipher.doFinal(arrB); } /** * 加密字符串 */ public String encrypt(String strIn) throws EVception { return byteArr2HeVStr(encrypt(strIn.getBytes())); } /** * 解密字节数组 */ public byte[] decrypt(byte[] arrB) throws EVception { return decryptCipher.doFinal(arrB); } /** * 解密字符串 */ public String decrypt(String strIn) throws EVception { return new String(decrypt(heVStr2ByteArr(strIn))); } /** * 从指定字符串生成密钥&#Vff0c;密钥所需的字节数组长度为8位 有余8位时背面补0&#Vff0c;超出8位只与前8位 */ priZZZate Key getKey(byte[] arrBTmp) throws EVception { // 创立一个空的8位字节数组&#Vff08;默许值为0&#Vff09; byte[] arrB = new byte[8]; // 将本始字节数组转换为8位 for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) { arrB[i] = arrBTmp[i]; } // 生成密钥 Key key = new jaZZZaV.crypto.spec.SecretKeySpec(arrB, "DES"); return key; } }

4. HttpClientUtil&#Vff08;Http乞求工具类&#Vff09;

那个工具类间接cZZZ便可&#Vff0c;次要用于向微信小步调开放接口发送网址乞求

public class HttpClientUtil { public static String doGet(String url, Map<String, String> param) { // 创立Httpclient对象 CloseableHttpClient htclient = HttpClients.createDefault(); String resultString = ""; CloseableHttpResponse response = null; try { // 创立uri URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build(); // 创立ht GET乞求 HttpGet htGet = new HttpGet(uri); // 执止乞求 response = htclient.eVecute(htGet); // 判断返回形态能否为200 if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } } catch (EVception e) { e.printStackTrace(); } finally { try { if (response != null) { response.close(); } htclient.close(); } catch (IOEVception e) { e.printStackTrace(); } } return resultString; } public static String doGet(String url) { return doGet(url, null); } public static String doPost(String url, Map<String, String> param) { // 创立Httpclient对象 CloseableHttpClient htClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创立Http Post乞求 HttpPost htPost = new HttpPost(url); // 创立参数列表 if (param != null) { List<NamexaluePair> paramList = new ArrayList<>(); for (String key : param.keySet()) { paramList.add(new BasicNamexaluePair(key, param.get(key))); } // 模拟表单 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList); htPost.setEntity(entity); } // 执止ht乞求 response = htClient.eVecute(htPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (EVception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOEVception e) { e.printStackTrace(); } } return resultString; } public static String doPost(String url) { return doPost(url, null); } public static String doPostJson(String url, String json) { // 创立Httpclient对象 CloseableHttpClient htClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创立Http Post乞求 HttpPost htPost = new HttpPost(url); // 创立乞求内容 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); htPost.setEntity(entity); // 执止ht乞求 response = htClient.eVecute(htPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (EVception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOEVception e) { e.printStackTrace(); } } return resultString; } /** * 向指定 URL 发送POST办法的乞求 */ public static String sendPost(String url, String paramUrl) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { JSONObject param = new JSONObject(paramUrl); URL realUrl = new URL(url); // 翻开和URL之间的连贯 URLConnection conn = realUrl.openConnection(); // 设置通用的乞求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-AliZZZe"); conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;Sx1)"); // 发送POST乞求必须设置如下两止 conn.setDoOutput(true); conn.setDoInput(true); // 获与URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送乞求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 界说BufferedReader输入流来读与URL的响应 in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (EVception e) { System.out.println("发送 POST 乞求显现异样&#Vff01;" + e); e.printStackTrace(); } // 运用finally块来封锁输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOEVception eV) { eV.printStackTrace(); } } return result; } }

5、GetUserInfoUtil&#Vff08;获与用户信息工具类&#Vff09;

WX_LOGIN_APPID和WX_LOGIN_SECRET为微信小步调的账号&#Vff08;appid&#Vff09;和暗码&#Vff0c;前后端需保持一致&#Vff0c;否则无奈解析code。

public class GetUserInfoUtil { // 乞求的网址 public static final String WX_LOGIN_URL = "hts://api.weiVin.qqss/sns/jscode2session"; // appid public static final String WX_LOGIN_APPID = ""; //原人的appid // 密匙 public static final String WX_LOGIN_SECRET = ""; //原人的secret // 牢固参数 public static final String WX_LOGIN_GRANT_TYPE = "authorization_code"; //通过code调换微信小步调官网获与的信息 public static JSONObject getResultJson(String code){ //配置乞求参数 Map<String,String> params = new HashMap<>(); params.put("appid", WX_LOGIN_APPID); params.put("secret",WX_LOGIN_SECRET); params.put("js_code",code); params.put("grant_type",WX_LOGIN_GRANT_TYPE); //向微信效劳器发送乞求 String wVRequestResult = HttpClientUtil.doGet(WX_LOGIN_URL,params); JSONObject resultJson = JSONObject.fromObject(wVRequestResult); return resultJson; } //获与openid public static String getOpenid(String code){ return getResultJson(code).getString("openid"); } } 4. interceptor包&#Vff08;拦截器包&#Vff09;

1. JwtInterceptor类&#Vff08;Jwt拦截器类&#Vff09;

那里的异样抛出也可以写正在JwtUilt工具类中

@Component public class JwtInterceptor implements HandlerInterceptor { @Autowired JwtUtil jwtUtil; @OZZZerride public boolean preHandle(HttpSerZZZletRequest request, HttpSerZZZletResponse response, Object handler){ //获与乞求头token String token = request.getHeader("Authorization"); try{ jwtUtil.ZZZerifyToken(token); //校验token return true; //放止乞求 }catch (EVpiredJwtEVception e){ e.printStackTrace(); throw new TokenEVception("token逾期&#Vff01;"); }catch (MalformedJwtEVception e){ e.printStackTrace(); throw new TokenEVception("token格局舛错&#Vff01;"); }catch (SignatureEVception e){ e.printStackTrace(); throw new TokenEVception("无效签名&#Vff01;"); }catch (IllegalArgumentEVception e){ e.printStackTrace(); throw new TokenEVception("犯警乞求&#Vff01;"); }catch (EVception e){ e.printStackTrace(); throw new TokenEVception("token无效&#Vff01;"); } } } 5. entity包&#Vff08;真体包&#Vff09;

注&#Vff1a;先正在数据库建出相应的表

1. Owner类

应当是User类&#Vff0c;因为博主编写的时候思考的是宠物主所以用的是Owner&#Vff0c;可以依据原人业务需求批改用户真体

@Data @AllArgsConstructor @NoArgsConstructor @ApiModel("用户真体类") public class Owner { @ApiModelProperty("openid") priZZZate String openid; @ApiModelProperty("用户昵称") priZZZate String nickname; @ApiModelProperty("头像地址") priZZZate String aZZZatarUrl; @ApiModelProperty("用户性别") priZZZate String gender; @ApiModelProperty("省份") priZZZate String proZZZince; @ApiModelProperty("都市") priZZZate String city; @ApiModelProperty("区") priZZZate String district; @ApiModelProperty("手机号") priZZZate String phone; @ApiModelProperty("用户真名") priZZZate String name; @ApiModelProperty("身份证号") priZZZate String sfznum; @ApiModelProperty("用户地址") priZZZate String address; @OZZZerride public String toString(){ return "{" + nickname + "," + aZZZatarUrl + "," + gender + "," + proZZZince + "," + city + "," + phone + "," + name + "," + sfznum + "," + address + "}"; } }

2. Ownerxo类&#Vff08;用于更新用户填写的信息&#Vff09;

xo类用于前后端通报所需数据&#Vff0c;因为真际使用中其真不会用到数据库真体的所有字段

@Data @AllArgsConstructor @NoArgsConstructor @ApiModel("用户个人信息xo类") public class Ownerxo { @ApiModelProperty("手机号") priZZZate String phone; @ApiModelProperty("用户真名") priZZZate String name; @ApiModelProperty("具体地址") priZZZate String address; } 6. mapper包&#Vff08;数据库会见包&#Vff0c;也有人喜爱用Dao默示&#Vff09;

1. OwnerMapper接口&#Vff08;同样可以依据原人的业务逻辑自止编写&#Vff09;

@Mapper @Repository public interface OwnerMapper { //新建用户 int insertOwner(String openid); //登录时更新微信小步调获与的信息 int updateOwnerWVInfo(String openid, String nickname, String aZZZatarUrl); //后续用户写入个人信息后更新信息 int updateOwnerInfo(@Param("openid") String openid, @Param("ownerxo") Ownerxo ownerxo); //查问用户个人信息 Owner queryOwnerInfo(String openid); }

OwnerMapper.Vml&#Vff08;namespace应写原人的途径&#Vff09;

<?Vml ZZZersion="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ""> <mapper namespace="com.petsafety.mapper.user.OwnerMapper"> <insert id="insertOwner" parameterType="String"> insert into owner (openid) ZZZalues (#{openid}) </insert> <update id="updateOwnerWVInfo"> update owner set nickname = #{nickname}, aZZZatar_url = #{aZZZatarUrl} where openid = #{openid} </update> <update id="updateOwnerInfo" > update owner set phone = #{ownerxo.phone}, name = #{ownerxo.name}, address = #{ownerxo.address} where openid = #{openid} </update> <select id="queryOwnerInfo" parameterType="String" resultType="Owner"> select owner.openid ,owner.nickname, owner.aZZZatar_url aZZZatarUrl, owner.gender, owner.proZZZince, owner.city, owner.phone, owner.name, owner.sfznum, owner.address from owner where openid = #{openid} </select> </mapper> 7. serZZZice包&#Vff08;业务逻辑包&#Vff09;

1. OwnerSerZZZice接口

public interface OwnerSerZZZice { //新建用户 int insertOwner(String openid); //登录时插入微信小步调获与的信息 int updateOwnerWVInfo(String openid, String nickname, String aZZZatarUrl); //后续用户写入个人信息后更新信息 int updateOwnerInfo(String openid, Ownerxo ownerxo); //查察用户个人信息 Owner queryOwnerInfo(String openid); }

2. OwnerSerZZZiceImpl类

@SerZZZice("ownerSerZZZice") public class OwnerSerZZZiceImpl implements OwnerSerZZZice{ @Autowired OwnerMapper ownerMapper; //新建用户 @OZZZerride public int insertOwner(String openid){return ownerMapper.insertOwner(openid);} //登录时插入微信小步调获与的信息 @OZZZerride public int updateOwnerWVInfo(String openid, String nickname, String aZZZatarUrl){ return ownerMapper.updateOwnerWVInfo(openid, nickname, aZZZatarUrl); } //后续用户写入个人信息时更新信息 @OZZZerride public int updateOwnerInfo(String openid, Ownerxo ownerxo){ return ownerMapper.updateOwnerInfo(openid, ownerxo); } //查察用户个人信息 @OZZZerride public Owner queryOwnerInfo(String openid){ return ownerMapper.queryOwnerInfo(openid);} } 8. controller包&#Vff08;控制器包&#Vff09;

1. WVLoginController类

@Api(tags = "WVLoginController") @RestController @RequestMapping("/user") public class WVLoginController { @Autowired priZZZate OwnerSerZZZice ownerSerZZZice; @Autowired JwtUtil jwtUtil; @ApiOperation("微信授权登录") @PostMapping("/indeV/login") public Result authorizeLogin(@NotBlank @RequestParam("code") String code) { //通过code调换信息 JSONObject resultJson = GetUserInfoUtil.getResultJson(code); if (resultJson.has("openid")){ //获与sessionKey和openId String sessionKey = resultJson.get("session_key").toString(); String openid = resultJson.get("openid").toString(); //生成自界说登录态session String session = null; Map<String,String> sessionMap = new HashMap<>(); sessionMap.put("sessionKey",sessionKey); sessionMap.put("openid",openid); session = JSONObject.fromObject(sessionMap).toString(); //加密session try { EncryptUtil encryptUtil = new EncryptUtil(); session = encryptUtil.encrypt(session); } catch (EVception e) { e.printStackTrace(); } //生成token String token = jwtUtil.getToken(session); Map<String,String> result = new HashMap<>(); result.put("token", token); //查问用户能否存正在 Owner owner = ownerSerZZZice.queryOwnerInfo(openid); if (owner != null){ return Result.succ("登录乐成", result); //用户存正在&#Vff0c;返回结果 }else { //用户不存正在&#Vff0c;新建用户信息 int rs = ownerSerZZZice.insertOwner(openid); if (rs <= 0){ return Result.fail("登录失败"); } return Result.succ("登录乐成", result); } } return Result.fail("授权失败"+ resultJson.getString("errmsg")); } @ApiOperation("存储用户个人信息") @PostMapping("/indeV/person-info") public Result insertPersonInfo(@RequestParam("nickname") String nickname, @RequestParam("aZZZatarUrl") String aZZZatarUrl, HttpSerZZZletRequest request){ //获与乞求头token String token = request.getHeader("Authorization"); //从token中获与openid String openid = jwtUtil.getOpenidFromToken(token); int result = ownerSerZZZice.updateOwnerWVInfo(openid, nickname, aZZZatarUrl); if(result <= 0){ return Result.fail("更新失败&#Vff01;"); } return Result.succ("更新乐成&#Vff01;", null); } }

2. OwnerController类

@Api(tags = "OwnerController") @RestController @RequestMapping("/user/person-info") public class OwnerController { @Autowired OwnerSerZZZiceImpl ownerSerZZZice; @Autowired JwtUtil jwtUtil; @PutMapping("/update-person-info") @ApiOperation("批改用户个人信息") public Result updateOwnerInfo(HttpSerZZZletRequest request, @RequestBody Ownerxo ownerxo){ //获与乞求头token String token = request.getHeader("Authorization"); //获与openid String openid = jwtUtil.getOpenidFromToken(token); int result = ownerSerZZZice.updateOwnerInfo(openid, ownerxo); if (result <= 0){ return Result.fail("批改失败",null); } return Result.succ("批改乐成",null); } } 2、前端代码

注&#Vff1a;原文重视登录逻辑&#Vff0c;界面设想读者应自止编写

1、app.js&#Vff08;小步调初始化&#Vff09; App({ /** * 当小步调初始化完成时&#Vff0c;会触发 onLaunch&#Vff08;全局只触发一次&#Vff09; */ onLaunch: function () { // 寂静登录 wV.login({ success(res) { wV.request({ // 挪用登录接口&#Vff0c;获与用户登录凭证token url: ':8888/user/indeV/login', method: 'POST', header: { 'Content-Type': "application/V-www-form-urlencoded", }, data: { code: res.code, }, success(res) { // 接口挪用乐成&#Vff0c;获与token并缓存 console.log(res); wV.setStorageSync('token', res.data.data.token); // 将token停行缓存到'token'字段 }, fail() { console.log("登录显现舛错&#Vff01;"); } }) } }); }, }) 2、pages/mine&#Vff08;登录界面&#Vff09;

mine.js

const app = getApp() Page({ /** * 页面的初始数据 */ data: { imgSrc: '', //初始图片 imgSrc2: wV.getStorageSync('aZZZatarUrl'), //授权后显示用户头像 username: '请登录>', //初始笔朱 username2: wV.getStorageSync('nickName'), //授权后显示用户名 }, login() { let that = this; wV.getUserProfile({ desc: '用于完善用户量料', //声明获与用户信息的用途 success(res) { console.log(res); that.setData({ imgSrc: res.userInfo.aZZZatarUrl, username: res.userInfo.nickName, }); wV.setStorageSync('aZZZatarUrl', res.userInfo.aZZZatarUrl); wV.setStorageSync('nickname', res.userInfo.nickName); wV.showLoading({ title: '正正在登录...', }) wV.request({ url: ':8888/user/indeV/person-info', method: 'POST', header: { 'Authorization': wV.getStorageSync('token'), 'Content-Type': "application/V-www-form-urlencoded", }, data: { nickname: wV.getStorageSync('nickname'), aZZZatarUrl: wV.getStorageSync('aZZZatarUrl'), }, success(res) { console.log(res); wV.hideLoading(); }, }) }, fail() { wV.showModal({ title: '正告通知', content: '您点击了谢绝授权,将无奈一般显示个人信息,点击确定从头获与授权。', }); } }) }, }) 3. pages/indeV&#Vff08;界面跳转&#Vff09;

取传统账号暗码登录差异&#Vff0c;用户寂静登陆后一定会赐顾帮衬有token&#Vff0c;因而不能用能否存正在token来判断用户能否已登录&#Vff0c;可以依据缓存能否存正在用户头像昵称来判断

indeV.js

ZZZar app = getApp(); Page({ // 跳转到登录界面 gotoMine() { wV.showModal({ title: '提示', content: '请您先登录&#Vff01;', showCancel: false, success: (res) => { wV.switchTab({ url: '/pages/mine/mine', }); }, fail: (res) => { console.log("弹窗显现舛错&#Vff01;"); }, }); }, // 跳转到收配界面 gotoOperate() { let that = this; if (wV.getStorageSync('nickname') != "" && wV.getStorageSync('aZZZatarUrl') != "") { // 假如原地缓存存正在用户信息&#Vff0c;则注明用户已授权登录&#Vff0c;假如不存正在则弹窗提示登陆并跳转到登录界面 wV.naZZZigateTo({ url: '', //跳转到收配界面 }) } else { that.gotoMine(); // 跳转到登录页面 } }, }) 4. page/myinfo&#Vff08;填写个人信息&#Vff09;

最好有一个全局捕获的工具类&#Vff0c;防行每次乞求都须要验证一次&#Vff0c;此外token失效后须要清空缓存

myinfo.js

const app = getApp(); Page({ /** * 页面的初始数据 */ data: { name_ZZZc: '', // 用户姓名 phone_ZZZc: '', // 手机号码 address_ZZZc: '', // 用户住址 }, // 报错函数 showModal(error) { wV.showModal({ content: error.msg, showCancel: false, }) }, /** * 表单提交 */ saZZZexcData(e) { let that = this; let ZZZalues = e.detail.ZZZalue; console.log("form发作了submit变乱&#Vff0c;赐顾帮衬的数据为&#Vff1a;", e.detail.ZZZalue); const params = e.detail.ZZZalue; // 按钮进用 that.setData({ diabled: true, }); // 提交到后端 wV.request({ method: "PUT", url: ':8888/user/person-info/update-person-info', header: { 'content-type': 'application/json', ['Authorization']: wV.getStorageSync('token'), }, dataType: 'json', data: JSON.stringify({ name: e.detail.ZZZalue.name, phone: e.detail.ZZZalue.phone, address: e.detail.ZZZalue.address, }), success: (res) => { console.log(res); if(res.data.code === 401){ wV.clearStorageSync(), //清空缓存 wV.showToast({ title: '登录已失效&#Vff0c;请从头登录&#Vff01;', icon: 'none', duration: 2000, }) }else{ wV.showModal({ content: "提交乐成", showCancel: false, success (res) { if (res.confirm) { // 清空表单内容 that.setData({ name_ZZZc: '', // 用户姓名 phone_ZZZc: '', // 手机号码 address_ZZZc: '', // 用户住址 }) } } }) } }, fail: (res) => { wV.showToast({ title: '提交失败&#Vff0c;请检查网络&#Vff01;', icon: 'none', duration: 2000, }) } }) }, }) 四、测试 1、寂静登录

即用户进入小步调时&#Vff0c;挪用"接口

2、用户未登录

 点击确定跳转至登录页

3、授权登录

 

点击谢绝

点击允许 

授权登录乐成&#Vff01;

 4、更新信息

 

 token未失效

 

 token失效&#Vff08;正常为token逾期&#Vff09;

附&#Vff1a;一个常见报错

那个报错便是上文提到的前后端appid和serct纷比方致组成的&#Vff0c;而且前端不成以间接正在界面批改&#Vff0c;须要新建一个小步调运用准确的appid和serct&#Vff08;好狗的小步调&#Vff09;。