准备工作
注册开发者资质
官网:https://open.weixin.qq.com/
尚硅谷分享
wx:
open:
# 微信开放平台 appid
appid: wxed9954c01bb89b47
# 微信开放平台 appsecret
appsecret: a7482517235173ddb4083788de60b90e
# 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)
redirecturl: http://guli.shop/api/ucenter/wx/callback
微信登陆流程
官网文档:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
微信登陆实现
配置文件
在 service_ucenter 模块的配置文件 application.properties 中,填写微信ID,密钥和域名地址。修改端口(同时修改nginx中的端口)
# 服务端口(在这里需要修改端口)
server.port=8160
# 用于微信登陆
# 微信开放平台 appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台 appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
# 微信开放平台 重定向url
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
创建 utils 包,然后创建类去读取配置文件中的值:
@Component
public class ConstantWxUtils 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;
public static String WX_OPEN_APP_ID;
public static String WX_OPEN_APP_SECRET;
public static String WX_OPEN_REDIRECT_URL;
@Override
public void afterPropertiesSet() {
WX_OPEN_APP_ID = appId;
WX_OPEN_APP_SECRET = appSecret;
WX_OPEN_REDIRECT_URL = redirectUrl;
}
}
生成扫描的二维码
直接请求微信提供的固定地址,然后向地址后面拼接参数。
在 service_ucenter 模块的 controller 层,创建类:
@CrossOrigin
@Controller
@RequestMapping("/api/ucenter/wx")
public class WxApiController {
@ApiOperation("生成微信扫描的二维码")
@GetMapping("/login")
public String getWxCode() {
// 微信开放平台授权baseUrl,%s是占位符
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";
// 对 redirect_url 进行 URLEncoder 编码
String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
try {
redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 设置值
String url = String.format(
baseUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
redirectUrl,
"atguigu"
);
// 重定向
return "redirect:" + url;
}
}
启动项目,访问:http://localhost:8006/api/ucenter/wx/login,效果如下:
扫码之后获取信息
步骤图
工具类
需要先引入依赖,不过在一开始的时候,在 service 模块的 pom.xml 中已经引入过了,所以不必重新引入。
在 utils 包中复制提供好的工具类:
HttpClientUtils.java
controller 层
WxApiController 类添加方法:
@CrossOrigin
@Controller
@RequestMapping("/api/ucenter/wx")
public class WxApiController {
@Autowired
private MemberService memberService;
@ApiOperation("生成微信扫描的二维码")
@GetMapping("/login")
public String getWxCode() {
// 微信开放平台授权baseUrl,%s是占位符
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";
// 对 redirect_url 进行 URLEncoder 编码
String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
try {
redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 设置值
String url = String.format(
baseUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
redirectUrl,
"atguigu"
);
// 重定向
return "redirect:" + url;
}
// 获取扫描人信息,添加数据
@ApiOperation("获取扫描人信息,添加数据")
@GetMapping("callback")
public String callback(String code, String state) {
// 获取code值(临时票据),类似于验证码
// 拿着 code 请求微信的固定地址,得到两个值 access_token 和 openid
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
// 拼接三个参数:id、密钥和 code 值
String accessTokenUrl = String.format(
baseAccessTokenUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
ConstantWxUtils.WX_OPEN_APP_SECRET,
code
);
// 请求这个拼接好的地址,得到返回的两个值
// 使用 httpclient 发送请求,得到返回结果
String accessTokenInfo;
try {
accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
} catch (Exception e) {
e.printStackTrace();
throw new GuliException(20001, "登陆失败");
}
// 从 accessTokenInfo 中 取出来 access_token 和 openid 并转换成 map,根据 key 获取对应值
Gson gson = new Gson();
Map mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
String access_token = (String) mapAccessToken.get("access_token");
String openid = (String) mapAccessToken.get("openid");
// 扫码人的信息添加到数据库中
// 根据 openid 判断数据库中是否存在相同的微信信息
UcenterMember member = memberService.getOpenIdMember(openid);
if (member == null) {
// 拿着得到的 access_token 和 openid 再去请求微信提供的地址,获得扫码人的信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
// 拼接两个参数
String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);
// 发送请求
String userInfo;
try {
userInfo = HttpClientUtils.get(userInfoUrl);
} catch (Exception e) {
e.printStackTrace();
throw new GuliException(20001, "登陆失败");
}
// 从 userInfo 中获取扫码人的信息
Map userInfoMap = gson.fromJson(userInfo, HashMap.class);
String nickname = (String) userInfoMap.get("nickname"); // 昵称
String headimgurl = (String) userInfoMap.get("headimgurl"); // 头像
member = new UcenterMember();
member.setOpenid(openid);
member.setNickname(nickname);
member.setAvatar(headimgurl);
memberService.save(member);
}
// 使用 JWT 根据 mapper 对象生成 token 字符串
String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
return "redirect:http://localhost:3000?token=" + jwtToken;
}
}
service 层
MemberService 类添加:
// 根据 openid 判断
UcenterMember getOpenIdMember(String openid);
实现类添加:
// 根据 openid 判断
@Override
public UcenterMember getOpenIdMember(String openid) {
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("openid", openid);
return baseMapper.selectOne(wrapper);
}
前端页面显示登陆信息
default.vue
修改 layouts/default.vue
<script>
import '~/assets/css/reset.css'
import '~/assets/css/theme.css'
import '~/assets/css/global.css'
import '~/assets/css/web.css'
import cookie from 'js-cookie'
import loginApi from '@/api/login'
export default {
data() {
return {
token: '',
loginInfo: {
id: '',
age: '',
avatar: '',
mobile: '',
nickname: '',
sex: ''
}
}
},
created() {
// 获取路径中的 token 值
this.token = this.$route.query.token
if (this.token) {
// 如果能取到就用微信扫码登陆
this.wxLogin()
}
this.showInfo()
},
methods: {
// 微信登陆显示的方法
wxLogin() {
// 把 token 放到 cookie
cookie.set('guli_token', this.token, { domain: 'localhost' })
cookie.set('guli_ucenter', '', { domain: 'localhost' })
// 调用接口,根据 token 值获取用户信息
loginApi.getLoginUserInfo().then(response => {
this.loginInfo = response.data.data.userInfo
cookie.set('guli_ucenter', this.loginInfo, { domain: 'localhost' })
})
},
// 退出的方法
logout() {
// 清空cookie值
cookie.set('guli_token', '', { domain: 'localhost' })
cookie.set('guli_ucenter', '', { domain: 'localhost' })
// 回到首页面
window.location.href = '/'
},
// 创建方法从cookie中获取信息
showInfo() {
// 从cookie中获取信息
var userStr = cookie.get('guli_ucenter')
// 转字符串转换成json对象(js对象)
if (userStr) {
this.loginInfo = JSON.parse(userStr)
}
}
}
}
</script>
测试
访问:http://localhost:8160/api/ucenter/wx/login
微信扫码登陆后,地址栏返回:http://localhost:3000/?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJndWxpLXVzZXIiLCJpYXQiOjE2MjExNTUzMTgsImV4cCI6MTYyMTI0MTcxOCwiaWQiOiIxMzkzODQ3NTQ3MDc1Nzg4ODAyIiwibmlja25hbWUiOiJKeXVua2FpIn0.-IdNv8SCo3L39BQkxDxRqlALst2JnXe3wLbTHX-dOYk
查看数据库中增加了一条信息: