微信授权登录网上一大堆,重复性很高
下面是自己总结的开发过程,已经简化
在微信公众号中>接口权限中获得相关的权限
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLwEleNVTWU1ENNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLxQzM4QTNzkDMzITOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
在修改中 设置网页授权域名,也就是你的回调地址,写域名即可,不需要http
然后填写域名,并下载校验文件,此文件必须能被访问到,不能被项目权限拦截
至于appid和私钥翻翻就能找到,开发项目
首先是获取code,获取有很多方式,第一种,前端传递给后台code;第二种后台直接获取,第二种比较规范,此文使用第一种,前端获取后再去请求后端,道理是一样的
第一种 前端获取 发送带有appid和回调地址的链接(地址为你前端随便一个截取code的地址,看你项目而定)>通过code去ajax请求后台地址>返回具体信息
第二种 后台获取 前端发送微信登录请求>后台拼接地址并重定向,redirect_url为前端截取code地址>通过code去ajax请求后台地址>返回具体信息
授权大致流程:获取code>获取openid、access_token、refresh_token>通过refresh_token刷新access_token>根据刷新后的access_token、openid获取用户信息(openid等参数意义看文档)
开始开发,此文使用第一种方式
第一步 前端发送带有参数链接获取code
body>
<h1>微信登录</h1>
<button onclick="clickSub()">点击登录</button>
</body>
<script>
function clickSub() {
alert("开始授权");
window.location.href=("https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的appid&redirect_uri=获取code的前台地址&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect");
}
第二步 通过redirect_uri重定向的地址截取code,每次请求(第一步)都会携带不同的code参数,可用微信开发工具查看地址栏变化,code是一次性的
<body>
<h1>登录成功</h1>
</body>
<script>
//截取code
function GetRequest() {
var url = location.search; //获取url中"?"符后的字符串
var theRequest = new Object();
if (url.indexOf("?") != -1) {
var str = url.substr(1);
strs = str.split("&");
for(var i = 0; i < strs.length; i ++) {
theRequest[strs[i].split("=")[0]]=(strs[i].split("=")[1]);
}
}
return theRequest;
}
//截取到的code
var Param= new Object();
Param= GetRequest();
var code = Param['code'];
//json格式请求后台
var datas = {"code":code};
$.ajax({
type: 'POST',
url: '.../api/wxlogin/auth',
contentType : 'application/json',
dataType : 'json',
data :JSON.stringify(datas),
success: function(data){
console.log("用户姓名:"+data.nickname)
},
error: function(result) {
alert("获取出错了!")
}
});
</script>
第三步 通过code获取access_token等信息
service
public static String APPID = "你的appid";
public static String APPSECRET = "你的APPSECRET";
public ResultResponseVo auth(String codes)throws Exception{
ResultResponseVo rrVo = new ResultResponseVo();
//将用户信息保存到map种
Map map = new HashMap();
try {
//第一步 通过code获取access_token
String code=codes;
if(null==code || "".equals(code)){
rrVo.setResult(HttpResultStatus.FAIL);
rrVo.setMsg("未获取code");
return rrVo;
}
// 根据code和appid和appsecret去获取openid和access_token、refresh_token
String url="https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + APPID+
"&secret=" +APPSECRET+
"&code=" +code+
"&grant_type=authorization_code";
//请求结果
String result1 = HttpClientManager.getUrlData(url);
//格式化
Map<String,Object> data1 = JSONObject.fromObject(result1);
String openid=data1.get("openid").toString();
if(null==openid || "".equals(openid)){
rrVo.setResult(HttpResultStatus.FAIL);
rrVo.setMsg("openid不能为空");
return rrVo;
}
String accessToken=data1.get("access_token").toString();
String refreshToken=data1.get("refresh_token").toString();
//第二步 刷新或续期access_token使用
String refresh_token = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+APPID +
"&grant_type=refresh_token" +
"&refresh_token="+refreshToken+"";
//请求结果
String result2 = HttpClientManager.getUrlData(refresh_token);
//格式化
Map<String,Object> data2 = JSONObject.fromObject(result2);
//最新openid
String infoopenid = data2.get("openid").toString();
//最新token
String access_token = data2.get("access_token").toString();
//第三步 获取用户个人信息(UnionID机制)
String infoUrl="https://api.weixin.qq.com/sns/userinfo?access_token=" +accessToken+
"&openid=" +infoopenid+
"&;
//请求结果
String result3 = HttpClientManager.getUrlData(infoUrl);
//格式化
//openid 用户唯一编号 nickname用户姓名等等
Map<String,Object> data3 = JSONObject.fromObject(result3);
map.put("openid",data3.get("openid").toString());
map.put("nickname",data3.get("nickname").toString());
map.put("headimgurl",data3.get("headimgurl").toString());
map.put("city",data3.get("city").toString());
System.out.println("用户姓名-nickName:"+data3.get("nickname").toString());
//下面可以开始你的项目逻辑,如保存到redis openid进来判断有无id,有就直接返回用户信息(数据库查询等等) 、保存到数据库用户信息等等
} catch (Exception e) {
e.printStackTrace();
rrVo.setResult(HttpResultStatus.FAIL);
rrVo.setMsg("服务器出错了");
return rrVo;
}
//将信息封装返回
rrVo.setData(map);
rrVo.setResult(HttpResultStatus.SUCCESS);
rrVo.setMsg(HttpResultStatus.SUCCESS.getMsg());
return rrVo;
}
HttpClientManager.java 网络请求类
public class HttpClientManager {
/**
* 执行url请求数据
*
* @param urlStr
* @return
* @throws Exception
* @throws
*/
public static String getUrlData(String urlStr) throws Exception {
URL url = new URL(urlStr);
URLConnection connection = url.openConnection();
// 一旦发送成功,用以下方法就可以得到服务器的回应:
String sCurrentLine = "";
StringBuffer sTotalString = new StringBuffer();
InputStream l_urlStream = connection.getInputStream();
BufferedReader l_reader = new BufferedReader(new InputStreamReader(
l_urlStream, "UTF-8"));
while ((sCurrentLine = l_reader.readLine()) != null) {
sTotalString.append(sCurrentLine);
}
return sTotalString.toString();
}
/**
* 执行url请求数据
*
* @param urlStr
* @return
* @throws Exception
* @throws
*/
public static String postUrlData(String urlStr, String data)
throws Exception {
URL url = new URL(urlStr);
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setRequestMethod("POST");
http.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒28
System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒29
// 30
OutputStream os = http.getOutputStream();
os.write(data.getBytes("UTF-8"));// 传入参数
os.flush();
os.close();
// 一旦发送成功,用以下方法就可以得到服务器的回应:
String sCurrentLine = "";
String sTotalString = "";
InputStream l_urlStream = http.getInputStream();
BufferedReader l_reader = new BufferedReader(new InputStreamReader(
l_urlStream, "UTF-8"));
while ((sCurrentLine = l_reader.readLine()) != null) {
sTotalString += sCurrentLine;
}
return sTotalString;
}
}
ResultResponseVo.java 封装信息类
public class ResultResponseVo implements Serializable {
private String code;//状态码
private String msg;//处理消息
private Object data = "";
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
/**
* 默认失败
*/
public ResultResponseVo() {
this.code = HttpResultStatus.FAIL.getCode();
this.msg = HttpResultStatus.FAIL.getMsg();
}
/**
* 指定默认Code
*
* @param enumItem
*/
public ResultResponseVo(HttpResultStatus enumItem) {
this.code = enumItem.getCode();
this.msg = enumItem.getMsg();
}
/**
* 设置调用结果
*
* @param enumItem
*/
public void setResult(HttpResultStatus enumItem) {
this.code = enumItem.getCode();
this.msg = enumItem.getMsg();
}
/**
* 设置调用结果
*/
public void setResult(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "ResultResponseVo{" +
"code='" + code + '\'' +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
Controller
@Controller
@RequestMapping(value="/api/wxlogin")
public class ApiWeixinLoginController {
private static Logger logger = LoggerFactory.getLogger(ApiWeixinLoginController.class);
@Resource(name = "apiWeixinLoginService")
private ApiWeixinLoginService apiWeixinLoginService;
/**
* 授权地址
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/auth")
public ResultResponseVo auth(@RequestBody String code) throws Exception{
ResultResponseVo rrVo = apiWeixinLoginService.auth(code);
return rrVo;
}
到此处简单的就结束了,有几个坑点
1.微信在文档中一直强调的 access_token和普通版本不同 ,普通版是指获取录音权限、支付权限等等的access_token,那个是需要缓存,有次数限制,微信授权的access_token是没有次数限制的,只有频率限制,因此不需要缓存,并且是一次性,有时候你会弄混,去缓存微信授权的access_token,适得其反,会报错
2.code只能使用一次,因此,在获取用户信息后必须在重新定向到第一步,再走一遍授权流程
3.如果前后端分离,需要跨域,跨域代码网上一大堆
等等