微信小程序登录流程图

个人实现登录流程的思路

一 前端调用wx.login() - 前端使用 wx.login()函数获取code, 每次获得的code仅可使用一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
wx.login({
success (res) {
if (res.code) {
//发起网络请求 将code发送到url后端
wx.request({
url: 'http://localhost/user/login',
data: {
code: res.code
}
})
} else {
console.log('登录失败!' + res.errMsg)
}
}
})

二 Controller中进行登录整个流程 - 首先封装包含 openId的用户, 为该用户生成jwt令牌, 将 user.id, openId, jwt的token封装成 userLoginVo返回前端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Autowired
private 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("userId",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);
}

三 wxLogin() - 位于ServiceImpl中, 用于返回包含openId的用户user - 通过getOpenId() 获取 openId, 如果用户是新用户,则创建此用户, 并加入openId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 /**
* 微信登录
*
* @param userLoginDTO
* @return
*/
@Override
public User wxLogin(UserLoginDTO userLoginDTO) {
// 调用微信接口服务, 获得当前微信用户的openid
String openId = getOpenId(userLoginDTO.getCode());
// 判断openid是否为空, 如果为空表示登陆失败, 则抛出业务异常
if (openId == null) {
throw new LoginFailedException("微信登录失败");
}
// 判断当前用户是否为新用户
User user = userMapper.selectByOpenid(openId);
// 如果为新用户, 则调用注册接口服务, 注册用户信息
if (user == null) {
user = User.builder()
.openid(openId)
.createTime(LocalDateTime.now())
.build();
userMapper.insert(user);
}

// 返回这个用户对象

return user;
}

``UserLoginDTO封装类

1
2
3
4
5
6
@Data
public class UserLoginDTO implements Serializable {

private String code;

}

四 getOpenId() - 位于ServiceImpl中 - 用于获取openId, 并返回openId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 微信登录接口地址
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";

@Autowired
private WeChatProperties weChatProperties;

/**
* 调用微信接口服务, 获得当前微信用户的openid
* @param code
* @return
*/
private String getOpenId(String code) {
// 调用微信接口服务, 获得当前微信用户的openid
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 body = HttpClientUtil.doGet(WX_LOGIN, map);

JSONObject jsonObject = JSON.parseObject(body);
String openid = jsonObject.getString("openid");
return openid;
}

此类中需要appid, secret, grant_type均可在微信开放平台获取到, js_code则是前端wx.login()函数获取到的code, 此时若获取到了openid,则微信登陆后端就算完成了

``weChatProperties 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 微信登录接口地址
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";

@Autowired
private WeChatProperties weChatProperties;

@Component
@ConfigurationProperties(prefix = "work.wechat")
@Data
public class WeChatProperties {

private String appid; //小程序的appid
private String secret; //小程序的秘钥
private String mchid; //商户号
private String mchSerialNo; //商户API证书的证书序列号
private String privateKeyFilePath; //商户私钥文件
private String apiV3Key; //证书解密的密钥
private String weChatPayCertFilePath; //平台证书
private String notifyUrl; //支付成功的回调地址
private String refundNotifyUrl; //退款成功的回调地址

}

在配置文件中加入 appie, secret数据

``application.yml 配置文件

1
2
3
wechat:
appid: ${sky.wechat.appid}
secret: ${sky.wechat.secret}

HttpClicentUtil封装类 getOpenId() 中的所调用的, 将 Httpclient的 doGet(), doPost()进行了封装, 便于调用

`` HttpClientUtil.java 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* Http工具类
*/
public class HttpClientUtil {

static final int TIMEOUT_MSEC = 5 * 1000;

/**
* 发送GET方式请求
* @param url
* @param paramMap
* @return
*/
public static String doGet(String url,Map<String,String> paramMap){
// 创建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();

//创建GET请求
HttpGet httpGet = new HttpGet(uri);

//发送请求
response = httpClient.execute(httpGet);

//判断响应状态
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;
}

/**
* 发送POST方式请求
* @param url
* @param paramMap
* @return
* @throws IOException
*/
public static String doPost(String url, Map<String, String> paramMap) throws IOException {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";

try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);

// 创建参数列表
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());

// 执行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;
}

/**
* 发送POST方式请求
* @param url
* @param paramMap
* @return
* @throws IOException
*/
public static String doPost4Json(String url, Map<String, String> paramMap) throws IOException {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";

try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);

if (paramMap != null) {
//构造json格式数据
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");
//设置请求编码
entity.setContentEncoding("utf-8");
//设置数据类型
entity.setContentType("application/json");
httpPost.setEntity(entity);
}

httpPost.setConfig(builderRequestConfig());

// 执行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();
}

}

至此后端实现微信登录的整个流程完成