准备
如不了解第三方登录流程,建议先大概了解一下,在来看看代码。
说明: 由于开放平台无测试号测试,所以只能上开放平台进行配置信息。公众平台的测试号并不能给开放平台使用。
微信开放平台地址:https://open.weixin.qq.com/cgi-bin/index?t=home/index&lang=zh_CN
配置步骤如下:
1、创建web应用
当创建成功的时候会有appid和appsecret,下面代码需要用到。
创建好之后需要设置回调域 ,就是当扫码通过之后会调用该域内的地址,地址在下面代码种配置。
2、注意!!!
1、设置回调域的时候,前面不能加上http://等协议。只需要配置域名即可,如baidu.com,spring.io等。填写成http://spring.io/是错误的
2、域名不能加端口号
代码
<一> 获取二维码
/**
* 跳转至微信登录界面
*/
// Result 定义的类,返回给前端的信息
@ApiOperation(value = "网站-微信扫码登录路径", notes = "网站-微信扫码登录路径")
@GetMapping(value = "/web/wxLoginPage")
public Result webWxKfLoginPage() throws Exception {
String url = WeChatLoginUtil.webWxKfLoginPage();
return Result.Builder.newBuilder(AppTipStatusEmum.SUCCESS_STATUS.getCode(), AppTipMsgEnum.SELECT_SUCCESS.getText()).setMessage("获取二维码成功!").setData(url).build();
}
涉及的WeChatLoginUtil类如下:
import com.uwa.mall.user.module.enums.ThirdPartyLoginEnum;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class WeChatLoginUtil {
// ===============================================web方式==================================================
// 网页方式登录(scope=snsapi_login)
public static String webWxKfLoginPage() throws Exception {
// 防止csrf攻击(跨站请求伪造攻击)
String state = UUID.randomUUID().toString().replaceAll("-", "");
// 存入redis。 CacheUtil为封装redis的工具类
CacheUtil.set(ThirdPartyLoginEnum.WeChatEnum.WeChatKFState.getCode() + state, "ok", Integer.valueOf(ThirdPartyLoginEnum.WeChatEnum.WeChatKFStateTime.getCode()));
// 普通微信三方地址
String url = ThirdPartyLoginEnum.WeChatEnum.AppUrl.getCode() + "appid=" + ThirdPartyLoginEnum.WeChatEnum.AppID.getCode() + "&redirect_uri=" + ThirdPartyLoginEnum.WeChatEnum.RedirectUri.getCode()
+ "&response_type=code&scope=snsapi_login&state=" + state + "#wechat_redirect";
return url;
}
/**
* 通过code获取token
*
* @return
*/
public static Map getAccessToken(String code, String appid, String secret) throws Exception {
// 构建请求数据
Map t = new HashMap();
t.put("appid", appid);
t.put("secret", secret);
t.put("code", code);
t.put("grant_type", "authorization_code");
// 调用httpclient处理请求得到返回json数据
String returnJson = HttpClientUtil.httpGet(ThirdPartyLoginEnum.WeChatEnum.AccessTokenUri.getCode(), t);
Map token = JsonUtil.fromJson(returnJson, Map.class);
return token;
}
/**
* 刷新token处理
*
* @return
*/
public static Map refreshToken(String appid, String refresh_token) throws Exception {
// 构建请求参数
Map m = new HashMap();
m.put("appid", appid);
m.put("grant_type", "refresh_token");
m.put("refresh_token", refresh_token);
String s = HttpClientUtil.httpGet(ThirdPartyLoginEnum.WeChatEnum.UrlRefresh.getCode(), m);
// 调用httpclient发出请求
Map refreshToken = JsonUtil.fromJson(s, Map.class);
return refreshToken;
}
/**
* 通过token获取用户信息
*
* @param token
* @return
*/
public static String getUserInfo(Map token) throws Exception {
// 构建请求数据
Map u = new HashMap();
u.put("access_token", token.get("access_token"));
u.put("openid", token.get("openid"));
u.put("lang", "zh_CN");
// 调用httpclient处理请求得到用户信息json数据
String userinfo = HttpClientUtil.httpGet(ThirdPartyLoginEnum.WeChatEnum.UrlUserInfo.getCode(), u);
return userinfo;
}
// ===============================================公众号方式==================================================
// 公众号方式登录(scope=snsapi_base)
public static String publicWxKfLoginPage() {
String state = UUID.randomUUID().toString().replaceAll("-", "");
CacheUtil.set(ThirdPartyLoginEnum.PublicWeChatEnum.WeChatKFState.getCode() + state, "ok", Integer.valueOf(ThirdPartyLoginEnum.PublicWeChatEnum.WeChatKFStateTime.getCode()));
String url = ThirdPartyLoginEnum.PublicWeChatEnum.AppUrl.getCode() + "appid=" + ThirdPartyLoginEnum.PublicWeChatEnum.AppID.getCode() + "&redirect_uri="
+ ThirdPartyLoginEnum.PublicWeChatEnum.RedirectUri.getCode() + "&response_type=code&scope=snsapi_userinfo&state=" + state + "#wechat_redirect";
return url;
}
// 公众号 通过code获取token
public static Map getAccessTokenByPublic(String code, String appid, String secret) throws Exception {
// 构建请求数据
Map t = new HashMap();
t.put("appid", appid);
t.put("secret", secret);
t.put("code", code);
t.put("grant_type", "authorization_code");
// 调用httpclient处理请求得到返回json数据
String returnJson = HttpClientUtil.httpGet(ThirdPartyLoginEnum.PublicWeChatEnum.AccessTokenUri.getCode(), t);
Map token = JsonUtil.fromJson(returnJson, Map.class);
return token;
}
public static Map refreshTokenByPublic(String appid, String refresh_token) throws Exception {
// 构建请求参数
Map m = new HashMap();
m.put("appid", appid);
m.put("grant_type", "refresh_token");
m.put("refresh_token", refresh_token);
String s = HttpClientUtil.httpGet(ThirdPartyLoginEnum.PublicWeChatEnum.UrlRefresh.getCode(), m);
// 调用httpclient发出请求
Map refreshToken = JsonUtil.fromJson(s, Map.class);
return refreshToken;
}
public static String getUserInfoByPublic(Map token) throws Exception {
// 构建请求数据
Map u = new HashMap();
u.put("access_token", token.get("access_token"));
u.put("openid", token.get("openid"));
u.put("lang", "zh_CN");
// 调用httpclient处理请求得到用户信息json数据
String userinfo = HttpClientUtil.httpGet(ThirdPartyLoginEnum.PublicWeChatEnum.UrlUserInfo.getCode(), u);
return userinfo;
}
}
涉及的ThirdPartyLoginEnum枚举接口类如下:
// 第三方登录方式
public interface ThirdPartyLoginEnum {
// 网页微信登录方式
enum WeChatEnum {
AppID("xxx.....xxx","微信开发平台的AppID"),
AppSecret("xxx.....xxx","微信开发平台的AppSecret"),
AppUrl("https://open.weixin.qq.com/connect/qrconnect?","微信第三方地址"),
RedirectUri("http://www.ycj.com/login/wxLogin","登录成功的回调地址"), //这里的域名和前面配置的域名必须一致,且不能加端口号
AccessTokenUri("https://api.weixin.qq.com/sns/oauth2/access_token","获取access_token的地址"),
UrlRefresh("https://api.weixin.qq.com/sns/oauth2/refresh_token","刷新获取微信token"),
UrlUserInfo("https://api.weixin.qq.com/sns/userinfo","获取用户信息"),
WeChatKFState("wechat-kf-state","redis的key"),
WeChatKFStateTime("300","redis的key的过期时间(秒)");
private String code;
private String msg;
WeChatEnum(String c, String m) {
this.code = c;
this.msg = m;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
// 公众号微信登录方式(移动端)
enum PublicWeChatEnum {
AppID("xxx.....xxx","微信公众平台的AppID"),
AppSecret("xxx.....xxx","微信公众平台的AppSecret"),
AppUrl("https://open.weixin.qq.com/connect/oauth2/authorize?","微信第三方地址"),
RedirectUri("http://activate.navicat.com/mall/user/api/loginAndRegister/public/wxLoginCallback","登录成功的回调地址"), //这里的域名和公众平台(不是本文的)配置的域名必须一致,且不能加端口号
AccessTokenUri("https://api.weixin.qq.com/sns/oauth2/access_token","获取access_token的地址"),
UrlRefresh("https://api.weixin.qq.com/sns/oauth2/refresh_token","刷新获取微信token"),
UrlUserInfo("https://api.weixin.qq.com/sns/userinfo","获取用户信息"),
WeChatKFState("wechat-kf2-state","redis的key"),
WeChatKFStateTime("300","redis的key的过期时间(秒)");
private String code;
private String msg;
PublicWeChatEnum(String c, String m) {
this.code = c;
this.msg = m;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
enum CacheParamEnum{
WXOpenID("WX_OpenID_","redis的key"),
WXOpenIDTime("300","redis的key的过期时间(秒)");
private String code;
private String msg;
CacheParamEnum(String c, String m) {
this.code = c;
this.msg = m;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
}
<二>扫码登录回调
// type有web和public两种类型,本文是开放平台,{type}使用的是web
@ApiOperation(value = "微信登录回调", notes = "微信登录回调")
@GetMapping("/{type}/wxLoginCallback")
public Result wxLoginCallback(@PathVariable("type") String type, @RequestParam(name = "code") String code, @RequestParam(name = "state", required = false) String state) throws Exception {
return thirdLoginService.wxLoginCallback(code, state, type);
}
涉及的thirdLoginService.wxLoginCallback接口代码如下:
// 微信登录回调
@Override
@Transactional(rollbackFor = Exception.class)
public Result wxLoginCallback(String code, String state, String type) throws Exception {
AssertException.isNull(code, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:code is empty,请重新登录!");
AssertException.isNull(state, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:state is empty,请重新登录!");
Map userMap = null;
int regType = RegisterTypeEnum.WX_WEB.getCode(); //枚举:web类型是开放平台,public是公众平台
if ("web".equals(type)) {
userMap = thirdGetUserInfoService.wxGetUserInfo(code, state); //获取用户信息
} else if ("public".equals(type)) {
regType = RegisterTypeEnum.WX_MP.getCode();
userMap = thirdGetUserInfoService.wxPublicGetUserInfo(code, state); //获取用户信息
} else {
AssertException.assertException(AppTipMsgEnum.LOGIN_FAIL.getCode(), "回调类型错误!");
}
AssertException.isNullObject(userMap, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败,请重新登录!");
AssertException.assertException(userMap.get("errcode") != null, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:" + userMap.get("errmsg") + ",请重新登录!");
AssertException.isNullObject(userMap.get("openid"), AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:openid为空!,请重新登录!");
// 以下是你自己的代码逻辑操作......
// 以下是你自己的代码逻辑操作......
// 以下是你自己的代码逻辑操作......
return Result.Builder.newBuilder(AppTipStatusEmum.SUCCESS_STATUS.getCode(), AppTipMsgEnum.SUCCESS.getText()).setData(objectMap).build();
}
涉及的thirdGetUserInfoService类的代码如下:
@Service("thirdGetUserInfoService")
public class ThirdGetUserInfoServiceImpl implements ThirdGetUserInfoService {
// 微信web方式获取用户信息
@Override
public Map wxGetUserInfo(String code, String state) throws Exception {
AssertException.isNull(code, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:code is empty,请重新登录!");
AssertException.isNull(state, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:state is empty,请重新登录!");
// 判断state是否合法
String stateStr = CacheUtil.get(ThirdPartyLoginEnum.WeChatEnum.WeChatKFState.getCode() + state);
AssertException.assertException(StringUtils.isEmpty(code) || StringUtils.isEmpty(state) || StringUtils.isBlank(stateStr), AppTipMsgEnum.PARAM_NOT_NULL.getCode(), "非法操作,请重新登录!");
// 用户授权后的code换取token
Map t_token = WeChatLoginUtil.getAccessToken(code, ThirdPartyLoginEnum.WeChatEnum.AppID.getCode(), ThirdPartyLoginEnum.WeChatEnum.AppSecret.getCode());
// 判断是否成功获取到了token
AssertException.isNullObject(t_token, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败,请重新登录!");
AssertException.assertException(t_token.get("errcode") != null, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:" + t_token.get("errmsg") + ",请重新登录!");
AssertException.assertException(StringUtils.isEmpty((String) t_token.get("access_token")) || StringUtils.isEmpty((String) t_token.get("openid")), AppTipMsgEnum.PARAM_NOT_NULL.getCode(),
"access_token拉取失败,请重新登录!");
// 刷新accesstoken
Map refreshToken = WeChatLoginUtil.refreshToken(ThirdPartyLoginEnum.WeChatEnum.AppID.getCode(), (String) t_token.get("refresh_token"));
AssertException.isNullObject(refreshToken, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败,请重新登录!");
AssertException.assertException(refreshToken.get("errcode") != null, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:" + refreshToken.get("errmsg") + ",请重新登录!");
// 使用token交换获取用户信息
String userInfo = WeChatLoginUtil.getUserInfo(refreshToken);
return JsonUtil.fromJson(userInfo, Map.class);
}
// 微信公众号方式获取用户信息
@Override
public Map wxPublicGetUserInfo(String code, String state) throws Exception {
AssertException.isNull(code, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:code is empty,请重新登录!");
AssertException.isNull(state, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:state is empty,请重新登录!");
// 判断state是否合法
String stateStr = CacheUtil.get(ThirdPartyLoginEnum.PublicWeChatEnum.WeChatKFState.getCode() + state);
AssertException.assertException(StringUtils.isEmpty(code) || StringUtils.isEmpty(state) || StringUtils.isBlank(stateStr), AppTipMsgEnum.PARAM_NOT_NULL.getCode(), "非法操作,请重新登录!");
// 用户授权后的code换取token
Map t_token = WeChatLoginUtil.getAccessTokenByPublic(code, ThirdPartyLoginEnum.PublicWeChatEnum.AppID.getCode(), ThirdPartyLoginEnum.PublicWeChatEnum.AppSecret.getCode());
// 判断是否成功获取到了token
AssertException.isNullObject(t_token, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败,请重新登录!");
AssertException.assertException(t_token.get("errcode") != null, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:" + t_token.get("errmsg") + ",请重新登录!");
AssertException.assertException(StringUtils.isEmpty((String) t_token.get("access_token")) || StringUtils.isEmpty((String) t_token.get("openid")), AppTipMsgEnum.PARAM_NOT_NULL.getCode(),
"access_token拉取失败,请重新登录!");
// 刷新accesstoken
Map refreshToken = WeChatLoginUtil.refreshTokenByPublic(ThirdPartyLoginEnum.PublicWeChatEnum.AppID.getCode(), (String) t_token.get("refresh_token"));
AssertException.isNullObject(refreshToken, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败,请重新登录!");
AssertException.assertException(refreshToken.get("errcode") != null, AppTipMsgEnum.LOGIN_FAIL.getCode(), "登录失败:" + refreshToken.get("errmsg") + ",请重新登录!");
// 使用token交换获取用户信息
String userInfo = WeChatLoginUtil.getUserInfoByPublic(refreshToken);
return JsonUtil.fromJson(userInfo, Map.class);
}
}
至此代码就算完成了,不足之处欢迎指教
由于框架用不明白,不想去理解,感觉配置太麻烦了,花那个时间去研究框架,我都写完了。
公众平台的登录请看另一篇:公众平台–扫描微信二维码,关注后自动登录