1、OAuth2

微信登录使用了OAuth2解决方案

1.1 OAuth2解决什么问题

1.1.1 开放系统间授权

照片拥有者想要在云冲印服务上打印照片,云冲印服务需要访问云存储服务上的资源

IMG_256

1.1.2图例

资源拥有者:照片拥有者

客户应用:云冲印

受保护的资源:照片

IMG_256

1.1.3方式一:用户名密码复制

IMG_258

用户将自己的”云存储”服务的用户名和密码,告诉”云冲印”,后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点。

(1)”云冲印”为了后续的服务,会保存用户的密码,这样很不安全。

(2)Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。

(3)”云冲印”拥有了获取用户储存在Google所有资料的权力,用户没法限制”云冲印”获得授权的范围和有效期。

(4)用户只有修改密码,才能收回赋予”云冲印”的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。

(5)只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。

总结:

将受保护的资源中的用户名和密码存储在客户应用的服务器上,使用时直接使用这个用户名和密码登录

适用于同一公司内部的多个系统,不适用于不受信的第三方应用

1.1.4方式二:通用开发者key

适用于合作商或者授信的不同业务部门之间

IMG_259

1.1.5方式三:颁发令牌

接近OAuth2方式,需要考虑如何管理令牌、颁发令牌、吊销令牌,需要统一的协议,因此就有了OAuth2协议

IMG_256

令牌类比仆从钥匙

IMG_256

1.2 OAuth2最简向导

1.2.1 OAuth主要角色

IMG_256

1.2.2最简向导

川崎高彦:OAuth2领域专家,开发了一个OAuth2 sass服务,OAuth2 as Service,并且做成了一个公司

在融资的过程中为了向投资人解释OAuth2是什么,于是写了一篇文章,《OAuth2最简向导》

1.3 OAuth2的应用

1.3.1 微服务安全

现代微服务中系统微服务化以及应用的形态和设备类型增多,不能用传统的登录方式

核心的技术不是用户名和密码,而是token,由AuthServer颁发token,用户使用token进行登录

IMG_256

1.3.2 社交登录

2、微信登录介绍

2.1 前期准备

1、注册

微信开放平台:https://open.weixin.qq.com

2、邮箱激活

3、完善开发者资料

4、开发者资质认证

准备营业执照,1-2个工作日审批、300元

5、创建网站应用

提交审核,7个工作日审批

6、内网穿透

ngrok的使用

2.2 授权流程

参考文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=e547653f995d8f402704d5cb2945177dc8aa4e7e&lang=zh_CN

获取access_token时序图

IMG_256

第一步:请求CODE(生成授权URL)

第二步:通过code获取access_token(开发回调URL)

3、使用教程

由于微信登录需要企业用户才能注册,这里我们使用的是尚硅谷的密钥

当前项目的启动端口设置为 8160 才可以获取到登录的二维码

3.1 配置文件中添加配置

1
2
3
4
5
6
7
8
# 微信开放平台appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
#回调的地址
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
#前端项目的地址
yygh.baseUrl=http://localhost:3000

3.2 编写配置类读取配置文件中的值

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
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ConstantPropertiesUtil implements InitializingBean {

@Value("${wx.open.app_id}")
private String appId;

@Value("${wx.open.app_secret}")
private String appSecret;

@Value("${wx.open.redirect_url}")
private String redirectUrl;

@Value("${yygh.baseUrl}")
private String yyghBaseUrl;

public static String WX_OPEN_APP_ID;
public static String WX_OPEN_APP_SECRET;
public static String WX_OPEN_REDIRECT_URL;
public static String YYGH_BASE_URL;

@Override
public void afterPropertiesSet() throws Exception {
WX_OPEN_APP_ID = appId;
WX_OPEN_APP_SECRET = appSecret;
WX_OPEN_REDIRECT_URL = redirectUrl;
YYGH_BASE_URL = yyghBaseUrl;
}
}

3.3 生成二维码和扫描二维码之后的回调操作

3.3.1 生成二维码

生成内嵌的二维码

返回给前端的相关参数

参数 是否必须 说明
self_redirect true:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri,false:手机点击确认登录后可以在 top window 跳转到 redirect_uri。默认为 false。
id 第三方页面显示二维码的容器id
appid 应用唯一标识,在微信开放平台提交应用审核通过后获得
scope 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
redirect_uri 重定向地址,需要进行UrlEncode
state 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验
style 提供”black”、”white”可选,默认为黑色文字描述。详见文档底部FAQ
href 自定义样式链接,第三方可根据实际需求覆盖默认样式。详见文档底部FAQ

生成扫描二维码的后端部分

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
import com.atguigu.yygh.common.result.Result;
import com.atguigu.yygh.user.utils.ConstantPropertiesUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;

/**
* @author GongChangjiang
* @version 1.0
* @Date 2023/4/20
* @Description 微信登录的控制器方法
*/
@Controller
@RequestMapping("/api/ucenter/wx")
@CrossOrigin
public class WxController {


/**
* 生成微信扫描二维码
* 返回给前端一些生成二维码的参数
*/
@GetMapping("/getLoginParameters")
@ResponseBody
public Result getLoginParameters() {
try {
HashMap<String, Object> map = new HashMap<>();
map.put("appid", ConstantPropertiesUtil.WX_OPEN_APP_ID);
map.put("scope", "snsapi_login");
map.put("redirect_uri", URLEncoder.encode(ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL, "utf-8"));
map.put("state", System.currentTimeMillis() + "");//原样返回,可以没有
return Result.ok(map);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return Result.fail();
}
}
}

生成扫描二维码的前端部分

JS部分

1
2
3
4
5
6
7
8
9
10
11
import request from '@/utils/request';

export default {
//获取生成二维码需要的参数信息
getLoginParam() {
return request({
url: `/api/ucenter/wx/getLoginParam`,
method: 'get'
})
}
}

引入api

1
import weixinApi from "@/api/weixin";

在mounted()方法中初始化微信js

1
2
3
4
5
6
7
mounted() {
//初始化微信js
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'
document.body.appendChild(script)
},

在页面中微信登录按钮绑定的是weixinLogin()点击事件,所以我们要在这个方法中初始化对象

向后端发起请求获取生成二维码需要的参数信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
weixinLogin() {
weixinApi.getLoginParam().then((response) => {
var obj = new WxLogin({
self_redirect: true,
id: "weixinLogin", // 需要显示的容器id <div id="weixinLogin"></div>
appid: response.data.appid, // appid
scope: response.data.scope, // 网页默认即可
redirect_uri: response.data.redirectUri, // 授权成功后回调的url
state: response.data.state, // 可设置为简单的随机数加session用来校验
style: "black", //。二维码的样式, 提供"black"、"white"可选
href: "", // 外部css文件url,需要https
});
});
},

点击微信登录之后,页面上的效果

image-20230421203717902

3.3.2 处理用户扫描成功之后的回调

实现的思路:

用户扫描二维码 -> 点击确认 -> 会执行回调请求(请求配置文件中这个地址wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback) -> 获取回调请求中携带的参数,再根据参数获取扫描用户的信息,执行相关的操作

扫描成功之后发起如下的请求:

image-20230421205258647

image-20230421210454720

引入httpclient(不依赖浏览器发起请求)依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>

导入HttpClientUtils工具类

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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
* @author GongChangjiang
* @version 1.0
* @Date 2023/4/21
* @Description
*/
public class HttpClientUtils {

public static final int connTimeout=10000;
public static final int readTimeout=10000;
public static final String charset="UTF-8";
private static HttpClient client = null;

static {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(128);
cm.setDefaultMaxPerRoute(128);
client = HttpClients.custom().setConnectionManager(cm).build();
}

public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{
return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
}

public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{
return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
}

public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,
SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}

public static String postParameters(String url, Map<String, String> params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}

public static String get(String url) throws Exception {
return get(url, charset, null, null);
}

public static String get(String url, String charset) throws Exception {
return get(url, charset, connTimeout, readTimeout);
}

/**
* 发送一个 Post 请求, 使用指定的字符集编码.
*
* @param url
* @param body RequestBody
* @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
* @param charset 编码
* @param connTimeout 建立链接超时时间,毫秒.
* @param readTimeout 响应超时时间,毫秒.
* @return ResponseBody, 使用指定的字符集编码.
* @throws ConnectTimeoutException 建立链接超时异常
* @throws SocketTimeoutException 响应超时
* @throws Exception
*/
public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
HttpClient client = null;
HttpPost post = new HttpPost(url);
String result = "";
try {
if (StringUtils.isNotBlank(body)) {
HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
post.setEntity(entity);
}
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
post.setConfig(customReqConf.build());

HttpResponse res;
if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(post);
} else {
// 执行 Http 请求.
client = HttpClientUtils.client;
res = client.execute(post);
}
result = IOUtils.toString(res.getEntity().getContent(), charset);
} finally {
post.releaseConnection();
if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
return result;
}


/**
* 提交form表单
*
* @param url
* @param params
* @param connTimeout
* @param readTimeout
* @return
* @throws ConnectTimeoutException
* @throws SocketTimeoutException
* @throws Exception
*/
public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
SocketTimeoutException, Exception {

HttpClient client = null;
HttpPost post = new HttpPost(url);
try {
if (params != null && !params.isEmpty()) {
List<NameValuePair> formParams = new ArrayList<NameValuePair>();
Set<Entry<String, String>> entrySet = params.entrySet();
for (Entry<String, String> entry : entrySet) {
formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
post.setEntity(entity);
}

if (headers != null && !headers.isEmpty()) {
for (Entry<String, String> entry : headers.entrySet()) {
post.addHeader(entry.getKey(), entry.getValue());
}
}
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
post.setConfig(customReqConf.build());
HttpResponse res = null;
if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(post);
} else {
// 执行 Http 请求.
client = HttpClientUtils.client;
res = client.execute(post);
}
return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
} finally {
post.releaseConnection();
if (url.startsWith("https") && client != null
&& client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
}

/**
* 发送一个 GET 请求
*/
public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)
throws ConnectTimeoutException,SocketTimeoutException, Exception {

HttpClient client = null;
HttpGet get = new HttpGet(url);
String result = "";
try {
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
get.setConfig(customReqConf.build());

HttpResponse res = null;

if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(get);
} else {
// 执行 Http 请求.
client = HttpClientUtils.client;
res = client.execute(get);
}

result = IOUtils.toString(res.getEntity().getContent(), charset);
} finally {
get.releaseConnection();
if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
return result;
}

/**
* 从 response 里获取 charset
*/
@SuppressWarnings("unused")
private static String getCharsetFromResponse(HttpResponse ressponse) {
// Content-Type:text/html; charset=GBK
if (ressponse.getEntity() != null && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
String contentType = ressponse.getEntity().getContentType().getValue();
if (contentType.contains("charset=")) {
return contentType.substring(contentType.indexOf("charset=") + 8);
}
}
return null;
}

/**
* 创建 SSL连接
* @return
* @throws GeneralSecurityException
*/
private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {
return true;
}
}).build();

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {

@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}

@Override
public void verify(String host, SSLSocket ssl)
throws IOException {
}

@Override
public void verify(String host, X509Certificate cert)
throws SSLException {
}

@Override
public void verify(String host, String[] cns,
String[] subjectAlts) throws SSLException {
}
});
return HttpClients.custom().setSSLSocketFactory(sslsf).build();

} catch (GeneralSecurityException e) {
throw e;
}
}
}


Controller

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
package com.atguigu.yygh.user.controller;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.common.result.Result;
import com.atguigu.yygh.user.utils.ConstantPropertiesUtil;
import com.atguigu.yygh.user.utils.HttpClientUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;

/**
* @author GongChangjiang
* @version 1.0
* @Date 2023/4/20
* @Description 微信登录的控制器方法
*/
@Controller
@RequestMapping("/api/ucenter/wx")
@CrossOrigin
public class WxController {


/**
* 生成微信扫描二维码
* 返回给前端一些生成二维码的参数
*/
@GetMapping("/getLoginParam")
@ResponseBody
public Result getLoginParameters() {
try {
HashMap<String, Object> map = new HashMap<>();
map.put("appid", ConstantPropertiesUtil.WX_OPEN_APP_ID);
map.put("scope", "snsapi_login");
map.put("redirect_uri", URLEncoder.encode(ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL, "utf-8"));
map.put("state", System.currentTimeMillis() + "");//原样返回,可以没有
return Result.ok(map);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return Result.fail();
}
}

/**
* 扫描之后回调的请求中携带了相关的参数,帮助我们获取用户的信息
* 登录成功之后的回调方法
* @param code 临时票据
* @param state 生成微信二维码之前put进去的参数 原样返回
* @return
*/
@GetMapping("/callback")
public String callback(String code ,String state){
//通过code、appid和app_secret去请求固定地址换取access_token
StringBuffer baseAccessTokenUrl = new StringBuffer()
.append("https://api.weixin.qq.com/sns/oauth2/access_token") //请求的固定地址
.append("?appid=%s") //%s占位符
.append("&secret=%s")
.append("&code=%s")
.append("&grant_type=authorization_code");

//拼接完整之后的地址
String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),
ConstantPropertiesUtil.WX_OPEN_APP_ID,
ConstantPropertiesUtil.WX_OPEN_APP_SECRET,
code);

//使用httpclient去请求这个地址换取access_token
try {
String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);//json格式的字符串
//从这个字符串中获取openid和 access_token的值
JSONObject jsonObject = JSONObject.parseObject(accessTokenInfo);
String accessToken = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
//根据access_token和openid获取微信用户的基本信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openid);
//发起请求获取用户信息
String userInfo = HttpClientUtils.get(userInfoUrl);//json格式的字符串
JSONObject parseObject = JSONObject.parseObject(userInfo);
//获取具体的用户信息
String nickname = parseObject.getString("nickname");//用户昵称
String sex = parseObject.getString("sex");//性别
String language = parseObject.getString("language");//语言
String city = parseObject.getString("city");//所在城市
String province = parseObject.getString("province");//省份
String country = parseObject.getString("country");//注册国家
String headimgurl = parseObject.getString("headimgurl");//头像的url地址
System.out.println("昵称:"+nickname+",性别:"+sex+",语言:"+language+",城市:"+city+",省份:"+province+",注册国家:"+country+",头像地址:"+headimgurl);
//TODO 将扫码人的信息添加到数据库中
} catch (Exception e) {
e.printStackTrace();
}
return "redirect:地址信息&参数信息(用户信息或者token信息等)";
}
}