Spring boot + Vue 微信第三方登录实践
2020-06-29 本文已影响0人
Epat
1. 流程图
第三方登录流程图
微信登录流程如下:
- 点击微信登录,新开窗口,跳转到微信扫码页面
- 用户扫码登录,新开窗口跳转到后台微信登录接口
- 后台判断用户是否成功登录,将新开窗口重定向到前台微信登录处理页面
- 微信登录处理页面会关闭新开窗口,判断用户是否成功登录,如果用户成功登录,跳转首页,如果用户登录失败,跳转注册页面
2. 前端代码核心逻辑实现
第一步 微信登录跳转逻辑
import URL from 'url-parse'
const WECHAT_BASE_URL = 'https://open.weixin.qq.com/connect/qrconnect'
const appId = "xxxxxx"
const redirectUri = "http://server:ip/wxLogin"
const responseType = "code"
const scope = "snsapi_login"
const wechatUrl = new URL(WECHAT_BASE_URL)
const windowHeight = window.screen.availHeight
const windowWidth = window.screen.availWidth
const height = 500
const width = 500
// 构建微信扫码页面链接
wechatUrl.set('query', {
appid: appId,
redirect_uri: encodeURI(redirectUri),
response_type: responseType,
scope,
state
})
wechatUrl.set('hash', 'wechat_redirect')
// 新窗口打开微信扫码页面
window.open(
wechatUrl.toString(),
'_blank',
`toolbar=no,directories=no,status=no,menubar=no,height=${height},width=${width},top=${(windowHeight - height) / 2},left=${(windowWidth - width) / 2}`
)
第二步 实现后端微信登录回调页面
- 在App.vue中新增微信登录回调方法
export default {
mounted () {
window.globalVue.$on('wechatLoginCallBack', function(result) {
console.log('监听到事件')
if (result.code === 200) {
// 微信登录成功
} else {
// 微信登录失败,用户不存在
}
})
}
}
- 编写后台微信登录回调页面
<template>
<div></div>
</template>
<script>
// 这是一个空页面,专门用来处理微信登录成功回调 访问路径为 /wechat/callback
export default {
mounted() {
this.init()
},
methods: {
init() {
const code = parseInt(this.$route.query.code)
const token = this.$route.query.token
const message = this.$route.query.message
// 调用微信登录回调方法
window.opener.globalVue.$emit('wechatLoginCallBack', {
code,
token,
message
})
this.$nextTick(() => {
self.close()
})
}
}
}
</script>
第三步 实现后台微信登录处理接口
实现微信登录Controller
@RequestMapping("/wechatOpenPlatform")
public class WechatOpenPlatformController{
@Autowired
WechatOauthService wechatOauthService;
@GetMapping("login")
@ApiOperation(value = "微信网页登录", notes = "微信网页登录")
public void login(@PathVariable String region, @NotNull(message = "微信登录参数错误") String code, HttpServletResponse response) {
wechatOauthService.wechatLogin(code, response);
}
}
实现微信登录service实现
public interface WechatOauthService {
/**
* 微信网页登录
*
* @param oauth2Handler
* @param callbackUrl
* @param authTokenForm
* @param response
*/
void wechatLogin(String code,
HttpServletResponse response);
}
@Service
public class WechatOauthServiceImpl implements WechatOauthService {
private static String callBackUrl = "http://www.xxxx.com/#/wechat/callback";
private static String openAppId= "xxxxx";
private static String openSecret= "xxxx";
@Autowired
UserService userService;
@Autowired
AutowireCapableBeanFactory autowireCapableBeanFactory;
@Override
public void wechatLogin (String code, HttpServletResponse response) {
WechatHandler wechatHandler= new WechatHandler(openAppId, openSecret);
// 实现容器外bean注入
autowireCapableBeanFactory.autowireBean(wechatHandler);
// 获取用户token
AuthTokenVo authTokenVo = wechatHandler.fetchToken(code);
// 根据token获取用户信息
WechatUserVo wechatUserVo = wechatHandler.fetchUserInfo(authTokenVo);
// 根据unionId判断用户是否存在
UserMo userMo = userService.selectUserByUnionId(wechatUserVo.getUnionId());
if (userMo == null) {
// 登录失败
response.sendRedirect(callBackUrl + "?code=500&token=" + wechatUserVo.getUnionId());
}
// 调用登录方法,写入cookie
userService.login(userMo);
// 登录成功
response.sendRedirect(callBackUrl + "?code=200");
}
}
// 调用微信接口获取微信信息处理类
public class WechatHandler {
/**
* 应用唯一标识
*/
private String appId;
/**
* 应用密钥
*/
private String secret;
/**
* 请求方法
*/
@Resource
RestTemplate restTemplate;
public WechatHandler(String appId, String secret) {
this.appId = appId;
this.secret = secret;
}
private final static String BASE_URL = "https://api.weixin.qq.com";
private final static String FETCH_TOKEN_GRANT_TYPE = "authorization_code";
private final static String TOKEN_URL =
BASE_URL + "/sns/oauth2/access_token?appid={appId}&secret={secret}&code={code}&grant_type={grantType}";
private final static String USER_INFO_URL = BASE_URL + "/sns/userinfo?access_token={accessToken}&openid={openId}";
@Override
public AuthTokenVo fetchToken(AuthTokenForm authTokenForm) {
Map<String, String> query = new HashMap<>();
query.put("appId", getAppId());
query.put("secret", getSecret());
query.put("code", authTokenForm.getCode());
query.put("grantType", FETCH_TOKEN_GRANT_TYPE);
String result = restTemplate.getForObject(TOKEN_URL, String.class, query);
JSONObject jsonObject = JSON.parseObject(result);
if (jsonObject.getInteger("errcode") != null) {
throw new BusinessException("获取TOKEN失败");
}
AuthTokenVo authTokenVo = new AuthTokenVo(jsonObject);
return authTokenVo;
}
@Override
public AuthTokenVo refreshToken(AuthRefreshTokenForm authRefreshTokenForm) {
return null;
}
@Override
public Object fetchUserInfo(AuthTokenVo authTokenVo) {
Map<String, String> query = new HashMap<>();
query.put("accessToken", authTokenVo.getAccessToken());
query.put("openId", authTokenVo.getOpenId());
String result = restTemplate.getForObject(USER_INFO_URL, String.class, query);
JSONObject jsonObject = JSON.parseObject(result);
if (jsonObject.getInteger("errcode") != null) {
throw new BusinessException("获取用户信息失败");
}
WechatUserVo wechatUserVo = new WechatUserVo(jsonObject);
return wechatUserVo;
}
}
微信用户实体类
@Data
@NoArgsConstructor
public class WechatUserVo {
/**
* OPEN_ID
*/
private String openId;
/**
* 唯一标识
*/
private String unionId;
/**
* 昵称
*/
private String nickName;
/**
* 性别
*/
private Integer sex;
/**
* 省份
*/
private String province;
/**
* 城市
*/
private String city;
/**
* 国家
*/
private String country;
/**
* 头像
*/
private String headImgUrl;
/**
* 特权信息
*/
private String privilege;
public WechatUserVo(JSONObject jsonObject) {
this.unionId = jsonObject.getString("unionid");
this.openId = jsonObject.getString("openid");
this.nickName = jsonObject.getString("nickname");
this.sex = jsonObject.getInteger("sex");
this.province = jsonObject.getString("province");
this.city = jsonObject.getString("city");
this.country = jsonObject.getString("country");
this.headImgUrl = jsonObject.getString("headimgurl");
this.privilege = jsonObject.getString("privilege");
}
}