准备
如不了解第三方登录流程,建议先大概了解一下,在来看看代码。
说明: 由于开放平台无测试号测试,所以只能上开放平台进行配置信息。公众平台的测试号并不能给开放平台使用。
微信开放平台地址: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获取tokenpublic 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方式获取用户信息@Overridepublic 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换取tokenMap t_token = WeChatLoginUtil.getAccessToken(code, ThirdPartyLoginEnum.WeChatEnum.AppID.getCode(), ThirdPartyLoginEnum.WeChatEnum.AppSecret.getCode());// 判断是否成功获取到了tokenAssertException.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拉取失败,请重新登录!");// 刷新accesstokenMap 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);}// 微信公众号方式获取用户信息@Overridepublic 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换取tokenMap t_token = WeChatLoginUtil.getAccessTokenByPublic(code, ThirdPartyLoginEnum.PublicWeChatEnum.AppID.getCode(), ThirdPartyLoginEnum.PublicWeChatEnum.AppSecret.getCode());// 判断是否成功获取到了tokenAssertException.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拉取失败,请重新登录!");// 刷新accesstokenMap 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);}
}
至此代码就算完成了,不足之处欢迎指教
由于框架用不明白,不想去理解,感觉配置太麻烦了,花那个时间去研究框架,我都写完了。
公众平台的登录请看另一篇:公众平台–扫描微信二维码,关注后自动登录