(十四)在线教育网站搭建-微信扫码登录❌注册登录

微信开发平台注册-调用微信扫码功能 https://open.weixin.qq.com/

1. 生成二维码

https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

1
2
3
4
5
6
# 微信开放平台 appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台 appsecret
wx.open.app_secret=48186e55d5270bc16b811bcfbacfec36
# 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)
wx.open.redirect_url=http://guli.shop/api/ucenter/wx/callback

控制器

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
package com.online.edu.memberservice.controller.front;

import com.google.gson.Gson;
import com.online.edu.memberservice.entity.UcenterMember;
import com.online.edu.memberservice.exception.EduException;
import com.online.edu.memberservice.service.UcenterMemberService;
import com.online.edu.memberservice.util.HttpClientUtils;
import com.online.edu.memberservice.util.WeixinProperties;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.UUID;

@Controller
@CrossOrigin
@RequestMapping("/api/ucenter/wx")
public class WeiXinController {

@Autowired
UcenterMemberService ucenterMemberService;


@GetMapping("/login")
public String login(HttpSession session){

// 微信开放平台授权baseUrl
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";


// 回调地址
String redirectUrl = WeixinProperties.WX_OPEN_REDIRECT_URL; //获取业务服务器重定向地址
try {
redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8"); //url编码
} catch (Exception e) {

}


//3、生成state参数
String state = "onwei"; //正式的生产环境中,需要生成一个随机字符串
// String state = UUID.randomUUID().toString().replaceAll("-", "");
session.setAttribute("wx-open-state", state);
System.out.println("生成 state = " + state);


//生成qrcodeUrl
String qrcodeUrl = String.format(
baseUrl,
WeixinProperties.WX_OPEN_APP_ID,
redirectUrl,
state);

return "redirect:"+qrcodeUrl;

}
}

2. 回调

扫描二维码,返回的是扫描之后的票据(code)

在回调方法中获取票据,请求微信地址来获取扫描人的信息凭证

根据凭证再请求微信地址获取扫描人信息

2.1. Maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.2</version>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>

<!--gson 用来转json-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>

2.2. /callback

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
/**
* 授权回调接口
*/
@GetMapping("callback")
public String callback(String code,
String state,
HttpSession session,
HttpServletRequest request,
HttpServletResponse response){

System.out.println("回调接口被调用");

System.out.println("授权临时票据 code = " + code);
System.out.println("state参数 state = " + state);

//判断回调参数是否合法
String stateStr = (String)session.getAttribute("wx-open-state");
System.out.println("session 中的state参 state = " + stateStr);


//发送通过code获取access_token的请求
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";

String accessTokenUrl = String.format(
baseAccessTokenUrl,
WeixinProperties.WX_OPEN_APP_ID,
WeixinProperties.WX_OPEN_APP_SECRET,
code
);

//向授权服务器申请授权码:向服务器发送远程http请求
String result = null;
try {
//获得响应的json字符串
result = HttpClientUtils.get(accessTokenUrl);
} catch (Exception e) {
e.printStackTrace();
}

//将json字符串转换成java对象
Gson gson = new Gson();
HashMap<String, Object> resultMap = gson.fromJson(result, HashMap.class);


//从返回结果中获取access_token和用户的openid
String accessToken = (String)resultMap.get("access_token");
String openid = (String)resultMap.get("openid");


System.out.println("accessToken = " + accessToken);
System.out.println("openid = " + openid);

//根据openid查询用户在数据库中是否存在
System.out.println("根据openid查询用户在数据库中是否存在");

UcenterMember member = ucenterMemberService.existWxUser(openid);

if(member == null){//新用户
System.out.println("注册微信新用户");

String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
String userInfoUrl = String.format(
baseUserInfoUrl,
accessToken,
openid);
//向微信服务器发送远程请求
String resultuserInfo = null;
try {
resultuserInfo = HttpClientUtils.get(userInfoUrl);
} catch (Exception e) {
e.printStackTrace();
}

HashMap<String, Object> resultUserInfoMap = gson.fromJson(resultuserInfo, HashMap.class);


String nickname = (String)resultUserInfoMap.get("nickname");
String headimgurl = (String)resultUserInfoMap.get("headimgurl");

member = new UcenterMember();
member.setNickname(nickname);
member.setAvatar(headimgurl);
member.setOpenid(openid);
memberService.save(member);
}
//跳转到网站的主页面
return "redirect:http://localhost:3000";
}

2.3. 并不能实现-invalid.appsecret

3. 用户注册-表单验证

https://element.eleme.cn/#/zh-CN/component/form

3.1. 前台

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
<template>
<div class="main">
<div class="title">
<a href="/user/login">登录</a>
<span>·</span>
<a class="active" href="#">注册</a>
</div>
<el-form :model="ucenterObj" label-position="right" status-icon :rules="rules" ref="ucenterObj" label-width="100px">
<el-form-item label="上传头像">
<el-upload
class="avatar-uploader"
action="http://localhost:9500/eduservice/upload?param=ucenter"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input type="text" v-model="ucenterObj.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model="ucenterObj.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPassword">
<el-input type="password" v-model="ucenterObj.checkPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="ucenterObj.sex">
<el-radio :label="1">女</el-radio>
<el-radio :label="2">男</el-radio>
</el-radio-group>
</el-form-item>

<el-form-item label="年龄" prop="age">
<el-input v-model.number="ucenterObj.age"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input type="text" v-model="ucenterObj.mobile"></el-input>
</el-form-item>
<el-form-item label="验证码" :inline="true" prop="code">
<el-input type="text" v-model="ucenterObj.code" style="width:50%;"></el-input>
<el-button type="primary" plain round @click="sendCode" v-text="codeText" :disabled="codeDisabeld"></el-button>
</el-form-item>
<el-form-item label="个性签名" prop="sign">
<el-input type="textarea" v-model="ucenterObj.sign" autocomplete="off"></el-input>
</el-form-item>

<el-form-item>
<el-button type="primary" @click="submitForm('ucenterObj')">提交</el-button>
<el-button @click="resetForm('ucenterObj')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>

<script>
import ucenter from '@/api/ucenter'
import '~/assets/css/sign.css'
import '~/assets/css/iconfont.css'

export default{
layout:'sign',

data(){
var checkAge = (rule, value, callback) => {
if (!value) {
return callback(new Error('年龄不能为空'))
}
setTimeout(() => {
if (!Number.isInteger(value)) {
callback(new Error('请输入数字值'))
} else {
callback()
}
}, 100);
}
var validatePass = (rule, value, callback) => {
if (value==='') {
callback(new Error('请输入密码'))
}else {
if(value.length<6){
callback(new Error('密码不宜短于6'))
}
if (this.ucenterObj.checkPassword !== '') {
this.$refs.ucenterObj.validateField('checkPassword')
}
callback()
}
};
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.ucenterObj.password) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
}
var validCode = (rule, value,callback)=>{

if (value === '') {
callback(new Error('请输入验证码'));
} else if (value !== this.code ){
callback(new Error('验证码不正确!'));
} else {
callback();
}

}
return{
ucenterObj:{
avatar:'',
nickname:'',
sex: 1,
password:'',
checkPassword:'',
mobile:'',
sign:'',
code:''

},
imageUrl: '',
uploadDisabled:false,
rules: {
nickname: [
{ required: true, message: '请输入昵称', trigger: 'blur' }
],
sex: [
{ required: true, message: '请选择您的性别', trigger: 'change' }
],
password: [
{ validator: validatePass, trigger: 'blur'}
],
checkPassword: [
{ validator: validatePass2, trigger: 'blur' }
],
age: [
{ validator: checkAge, trigger: 'blur' }
],
mobile: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ min:11, max: 11, message: '手机号11位', trigger: 'blur' }
],
sign:[
{ required: true, message: '请输入您的个性签名', trigger: 'blur' }
],
code:[
{ validator: validCode, trigger: 'blur' }
]

},
codeText:'获取验证码',
codeDisabeld:false ,
count:'',
timer:null ,
code:''

}
},
methods: {
getMobileCode(phone){
ucenter.getMobileCode(phone).then(response=>{
this.code=response.data.data.code
this.$alert(this.code, '验证码', {
confirmButtonText: '确定'
})
})
},
handleAvatarSuccess(response, file) {
this.$message(response.message)
this.imageUrl = response.data.path
//this.uploadDisabled=true
this.ucenterObj.avatar=response.data.path
},
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isLt2M = file.size / 1024 / 1024 < 2;

if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!')
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!')
}
return isJPG && isLt2M;
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
ucenter.registerUcenter(this.ucenterObj).then(response=>{
this.$message({
type:'success',
message:response.message
})
this.$router.push({
path:'/user/login'
})
}).catch(response=>{
this.$message({
type:'error',
message:response.message
})
})
} else {
//console.log('error submit!!');
return false;
}
})
},
resetForm(formName) {
this.$refs[formName].resetFields()
},
sendCode(){
if(this.ucenterObj.mobile===''){
this.$message({
type:'error',
message:'手机号不能为空'
})
}else{
this.getMobileCode(this.ucenterObj.mobile);

const TIME_COUNT = 60
if(!this.timer){
this.count=TIME_COUNT
this.codeDisabeld=true
this.timer=setInterval(() => {
if(this.count>0 &&this.count<=TIME_COUNT){
this.count--
this.codeText='( '+this.count+' )秒后重新获取'
}else{
this.codeDisabeld=false
clearInterval(this.timer)
this.timer=null
this.codeText='重新获取验证码'
}
}, 1000)
}
}
}
}
}
</script>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 108px;
height: 108px;
line-height: 108px;
text-align: center;
}
.avatar {
width: 108px;
height: 108px;
display: block;
}
</style>

3.2. ❌手机验证码服务 - 腾讯云

https://cloud.tencent.com/product/sms

需要注册得到appid,appsecret

还需要认证 。。。。。。。。

https://cloud.tencent.com/document/product/382/13613

3.2.1. Maven

1
2
3
4
5
<dependency>
<groupId>com.github.qcloudsms</groupId>
<artifactId>qcloudsms</artifactId>
<version>1.0.6</version>
</dependency>

3.2.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
import com.github.qcloudsms.SmsSingleSender;
import com.github.qcloudsms.SmsSingleSenderResult;
import com.github.qcloudsms.httpclient.HTTPException;
import org.json.JSONException;
import java.io.IOException;
try {
// 短信应用 SDK AppID
int appid = 1400009099; // SDK AppID 以1400开头
// 短信应用 SDK AppKey
String appkey = "9ff91d87c2cd7cd0ea762f141975d1df37481d48700d70ac37470aefc60f9bad";
// 需要发送短信的手机号码
String[] phoneNumbers = {"21212313123", "12345678902", "12345678903"};
// 短信模板 ID,需要在短信应用中申请
int templateId = 7839; // NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请
// 签名
String smsSign = "腾讯云"; // NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台申请
String[] params = {"5678"};
SmsSingleSender ssender = new SmsSingleSender(appid, appkey);
SmsSingleSenderResult result = ssender.sendWithParam("86", phoneNumbers[0],
templateId, params, smsSign, "", "");
System.out.println(result);
} catch (HTTPException e) {
// HTTP 响应码错误
e.printStackTrace();
} catch (JSONException e) {
// JSON 解析错误
e.printStackTrace();
} catch (IOException e) {
// 网络 IO 错误
e.printStackTrace();
}

3.3. 验证码放入redis中,存1分钟

3.3.1. 连接redis

1
2
3
#redis
spring.redis.host=127.0.0.1
spring.redis.port=6379

3.3.2. 生成4位验证码

3.3.2.1. controller-通过响应验证码显示在前台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.online.edu.memberservice.controller.front;

import com.online.edu.common.R;
import com.online.edu.memberservice.entity.front.UcenterObj;
import com.online.edu.memberservice.service.UcenterMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/memberservice/front/ucenter-member")
@CrossOrigin
public class UcenterMemberFrontController {

@Autowired
UcenterMemberService ucenterMemberService;

@GetMapping("/getMobileCode/{phone}")
public R getPhoneCode(@PathVariable String phone){
String code = ucenterMemberService.getPhoneCode(phone);
return R.ok().data("code",code);
}
}

3.3.2.2. service-验证码存入redis

1
2
3
4
5
6
7
8
9
10
11
12
13
//得到短信验证码

@Override
public String getPhoneCode(String phone) {

//生成4位验证码
String code="";
for(int i=0;i<4;i++){
code+=(int)(Math.random()*10)+"";
}
redisTemplate.opsForValue().set(phone,code,1, TimeUnit.MINUTES);
return code;
}

3.4. 前台验证码按钮事件

3.4.1. ucenter.js

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

const base ='/memberservice/front/ucenter-member'
export default{

getMobileCode(phone){
return request({
url: `${base}/getMobileCode/`+phone,
method:'get'
})
}
}

3.4.2. pages/user/register.vue

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
<el-form-item label="手机号" prop="mobile">
<el-input type="text" v-model.number="ucenterObj.mobile"></el-input>
</el-form-item>

<el-button type="primary" plain round @click="sendCode('ucenterObj')" v-text="codeText" :disabled="codeDisabeld"></el-button>

<script>
import ucenter from '@/api/ucenter'
export default{
data(){
return{
codeText:'获取验证码',
codeDisabeld:false ,
count:'',
timer:null ,
code:''
}
}
methods:{
getMobileCode(phone){
ucenter.getMobileCode(phone).then(response=>{
this.code=response.data.data.code
this.$alert(this.code, '验证码', {
confirmButtonText: '确定'
})
})
},
sendCode(formName){
//验证手机号输入框
this.$refs[formName].validateField('mobile',(valid) => {
//console.log(valid)
if(valid.length===0){
//查询已注册的手机号
ucenter.existMobile(this.ucenterObj.mobile).then(response=>{

if(response.data.success===false){
this.$message({
type:'error',
message:'该手机号已注册'
})
}else{
//发送手机号
this.getMobileCode(this.ucenterObj.mobile);
const TIME_COUNT = 60
if(!this.timer){
this.count=TIME_COUNT
this.codeDisabeld=true
this.timer=setInterval(() => {
if(this.count>0 &&this.count<=TIME_COUNT){
this.count--
this.codeText='( '+this.count+' )秒后重新获取'
}else{
this.codeDisabeld=false
clearInterval(this.timer)
this.timer=null
this.codeText='重新获取验证码'
}
}, 1000)
}

}
})

}else {
//console.log('error submit!!');
return false;
}
})
}
}
}
</script>

3.5. 密码采用md5加密

1
2
3
4
5
6
7
import org.springframework.util.DigestUtils;

//密码进行编码
String code=DigestUtils.md5DigestAsHex(member.getPassword().getBytes());
//System.out.println(code);
//e10adc3949ba59abbe56e057f20f883e
member.setPassword(code);

4. 登录验证-jwt

4.1. jwt依赖

1
2
3
4
5
6
7
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>

<jwt.version>0.9.1</jwt.version>

4.2. jwtUtils

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
package com.online.edu.memberservice.util;

import com.online.edu.memberservice.entity.UcenterMember;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import java.util.Date;

public class JwtUtils {

public static final String SUBJECT = "online-user";

//秘钥
public static final String APPSECRET = "login666";

//过期时间,毫秒,30分钟
public static final long EXPIRE = 1000 * 60 * 30;

/**
* 生成Jwt令牌
* @param member
* @return
*/
public static String generateJWT(UcenterMember member){

if(member == null
|| StringUtils.isEmpty(member.getId())
|| StringUtils.isEmpty(member.getNickname())
|| StringUtils.isEmpty(member.getAvatar())){
return null;
}

String token = Jwts.builder()
.setSubject(SUBJECT)
.claim("id", member.getId())
.claim("nickname", member.getNickname())
.claim("avatar", member.getAvatar())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.signWith(SignatureAlgorithm.HS256, APPSECRET).compact();

return token;
}


/**
* 校验jwt
* @param jwtToken
* @return
*/
public static Claims checkJWT(String jwtToken){

Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APPSECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();

return claims;
}
}

4.3. 当验证用户密码后,匹配则返回token

4.3.1. controller

1
2
3
4
5
6
7
8
9
10
@GetMapping("/login/{mobile}/{password}")
public R login(@PathVariable String mobile,
@PathVariable String password){
String token = ucenterMemberService.login(mobile,password);
if(token!=null) {
return R.ok().data("token",token);
}else{
return R.error().message("密码错误");
}
}

4.3.2. service

验证手机号和加密的密码是否匹配,将id,头像,昵称进行编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public String login(String mobile, String password) {

QueryWrapper<UcenterMember> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("mobile",mobile);

String md5 = DigestUtils.md5DigestAsHex(password.getBytes());
queryWrapper.eq("password",md5);
UcenterMember member =baseMapper.selectOne(queryWrapper);

String token= JwtUtils.generateJWT(member);

if(member!=null){

return token;

}
return null;
}

4.4. 前台判断响应的数据,实现跳转

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
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
ucenter.existMobile(this.loginObj.mobile).then(response=>{
//当数据库中手机号不存在,则说明手机号未注册--刚好和注册时判断相反
this.code=response.data.code
if(this.code===20000){
this.$message({
type:'error',
message:'该手机号未注册'
})
}else{
ucenter.login(this.loginObj.mobile,this.loginObj.password).then(response=>{
//console.log(response)
if(!response.data.success){
this.$message({
type:'error',
message: response.data.message
})
}else{
this.$message({
type:'success',
message:'登录成功'
})
this.$router.push({
path:'/?token='+response.data.data.token
})
}
})
}
})
} else {
//console.log('error submit!!');
return false;
}
})

4.5. 解析jwt得到id,avatar,nickname

4.5.1. controller

1
2
3
4
5
6
@GetMapping("/getToken/{token}")
public R getToken(@PathVariable String token){

Map<String,Object> map = ucenterMemberService.getTokenInfo(token);
return R.ok().data("userInfo",map);
}

4.5.2. service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public Map<String, Object> getTokenInfo(String token) {

//解码jwt
Claims claims = JwtUtils.checkJWT(token);
String nickname =(String)claims.get("nickname");
String avatar=(String)claims.get("avatar");
String id=(String)claims.get("id");

Map<String,Object> map = new HashMap<>();
map.put("nickname",nickname);
map.put("avatar",avatar);
map.put("id",id);

return map;
}

4.5.3. layout/default.vue

token是拼接在路由后,get提交,长度有限,同时不需要存储cookie,

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
<!-- / nav -->
<ul class="h-r-login">
<li v-if="!userInfo.id" id="no-login">
<a href="/user/login" title="登录">
<em class="icon18 login-icon">&nbsp;</em>
<span class="vam ml5">登录</span>
</a>
|
<a href="/user/register" title="注册">
<span class="vam ml5">注册</span>
</a>
</li>
<li v-if="userInfo.id" id="is-login-two" class="h-r-user">
<a href="#" title>
<img
:src="imageUrl"
width="30"
height="30"
class="vam picImg"
alt
>
<span id="userName" class="vam disIb" style="max-width:100%">{{ userInfo.nickname }}</span>
</a>
<a href="/" title="退出" class="ml5">退出</a>
</li>
<!-- /未登录显示第1 li;登录后显示第2 li -->
</ul>
<script>

import ucenter from '@/api/ucenter'

export default {
data(){
return{
userInfo:{
avatar:'',
nickname:'',
id:''
},
imageUrl:'/_nuxt/assets/img/avatar-boy.gif',
token:''
}
},
created(){
this.token = this.$route.query.token
//当跳转无token时不调用方法
if(this.token) {
this.getUserInfo(this.token)
}

},
methods:{
getUserInfo(token){
ucenter.getToken(token).then(response=>{
//console.log(response)
const id = response.data.data.userInfo.id
//当token不过期时赋值
if(id){
this.imageUrl=response.data.data.userInfo.avatar
this.userInfo.nickname=response.data.data.userInfo.nickname
this.userInfo.id = response.data.data.userInfo.id
}else{
this.$router.push({ path: '/' })
}
//console.log(this.userInfo)
})
}

}
}
</script>

5. 将jwt存入cookie,让每个请求头都带上

5.1. 安装vue-cookies

https://www.npmjs.com/package/vue-cookies

nuxt中引入插件

1
npm install vue-cookies --save

5.2. 引入插件

在plugins文件夹下新建vue-cookie.js,输入

1
2
3
import Vue from 'vue'
import VueCookies from 'vue-cookies'
Vue.use(VueCookies)

在nuxt.config.js

1
2
3
4
5
6
7
/*
** Plugins to load before mounting the App
*/
plugins: [
'@/plugins/element-ui',
{ src: '~/plugins/vue-cookie.js', ssr: false }
],

5.3. 登录脚本中存入cookie

this.$cookies.set(“logintoken”,token,’30MIN’)

默认有效期30分钟

1
2
3
4
5
6
7
8
9
10
11
12
13
ucenter.login(this.loginObj.mobile,this.loginObj.password).then(response => {
console.log(response)
const token = response.data.data.token
if(response.data.success){
this.$message({
type:'success',
message:'登录成功'
})

this.$cookies.set("logintoken",token,'30MIN')
this.$router.push({ path: this.redirect || '/' })
}
}

5.4. 将cookie取出让每个请求头携带

utils/request.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { getToken } from '@/utils/auth'

service.interceptors.request.use(
config => {
// do something before request is sent
//console.log(config)

// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
const token = getToken()
if(token !==undefined){
config.headers['logintoken'] = token
}

return config
},
error => {
// do something with request error
//console.log(error) // for debug
return Promise.reject(error)
}
)

5.5. jwt过期,提示重新登录

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
// response interceptor
service.interceptors.response.use(
response => {
const res = response.data
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('游客可能无法使用某些功能(支付,观看视频,评论等)', '确认登录', {
confirmButtonText: '登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//Router.push({ path: '/user/login' })
location.href='http://localhost:3000/user/login'
}).catch(()=>{
//location.href=location.href
})
}

return response

},
error => {
// console.log('err' + error) // for debug
// Message({
// message: error.message,
// type: 'error',
// duration: 5 * 1000
// })
return Promise.reject(error)
}
)

5.6. 后台通过请求是否携带logintoken请求头判断用户登录

本文结束  感谢您的阅读