实现业务:微信授权登录,调用后台接口,获取用户信息,实现自己系统中的用户登录业务。
Ⅰ.开发前准备一网页授权回调域名
首先要在公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名,微信公众平台测试号支持本地ip,这里在微信测试号中测试,微信公众平台测试地址
具体而言,微信浏览器中网页授权流程分为四步
1.用户进入授权页面,同意授权,获取code
2.通过code换取网页授权access_token(与基础支持中的access_token不同)
3.如果需要,开发者可以刷新网页授权access_token,避免过期
4.通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
Ⅱ.具体开发流程
这里采用拦截器形式对需要用户信息的业务进行拦截,实现自己系统中的用户登录业务
package com.jianshu.web.interceptor;
import com.jianshu.common.utils.Constants;
import com.jianshu.common.utils.StringUtils;
import com.jianshu.common.utils.SystemKeys;
import com.jianshu.modules.frontBase.model.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
public class OAuth2Interceptor extends HandlerInterceptorAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(OAuth2Interceptor.class);
// 微信授权地址
private static String authUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
// 微信授权方式 :应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)
// snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,
// 即使在未关注的情况下,只要用户授权,也能获取其信息
private static String SCOPE = "snsapi_userinfo";
// private static String SCOPE = "snsapi_base";
//微信授权获取登录凭证code接口
private static String REDIRECT_URI = Constants.CONTEXT_DOMAIN + "wow/auth?redirect=REDIRECT_URL";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Cookie[] cookies = request.getCookies();
String openid = "";
if (cookies != null) {
//这里是根据cookie的key获取value
openid = SystemKeys.getUserInfo(cookies);
}
if (StringUtils.isBlank(openid)) {
LOGGER.debug("###### wechat auth login !");
String queryString = request.getQueryString();
String redirectUrl = "";
if(StringUtils.isNotBlank(queryString)){
redirectUrl = request.getRequestURL() + "?" + request.getQueryString();
}
String url = authUrl.replace("APPID", Constants.APPID).replace("SCOPE", SCOPE).replace("REDIRECT_URI", URLEncoder.encode(REDIRECT_URI.replace("REDIRECT_URL", redirectUrl), "utf-8"));
response.sendRedirect(url);
return false;
}
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
微信授权获取登录凭证code接口
@RequestMapping("wow/main")
public String main(HttpServletRequest request, HttpServletResponse response, Model model) {
return "web/main/index";
}
@RequestMapping("wow/auth")
public String auth(HttpServletRequest request, HttpServletResponse response) {
String code = request.getParameter("code");
String openid = "";
String redirectUrl = request.getParameter("redirect");
try {
String res = WXUtils.getOpenId(Constants.APPID, Constants.APPSECRET, code);
LOGGER.info("openId from wx res:" + res);
JSONObject json = JSONObject.fromObject(res);
openid = json.getString("openid");
String token = json.getString("access_token");
if (StringUtils.isNotEmpty(openid)) {
//用户登录
User user = apiUserService.login(openid, token);
if (user != null) {
//用户信息存储到cookie
Cookie userCookie = new Cookie(Constants.User.COOKIE_KEY, openid);
userCookie.setMaxAge(-1); //设置为“0”或负值时,关闭浏览器才会清除cookie
userCookie.setPath("/");
response.addCookie(userCookie);
}
}
} catch (Exception e) {
LOGGER.error("程序错误", e);
return "web/common/error";
}
if (StringUtils.isBlank(redirectUrl)) {
//redirect 地址为空的话默认跳转到主页
redirectUrl = "/wow/main";
}
return "redirect:" + redirectUrl;
}
自己系统中的用户登录业务逻辑
package com.jianshu.web.frontBase.service;
import com.jianshu.common.service.BaseService;
import com.jianshu.common.utils.*;
import com.jianshu.modules.frontBase.dao.UserDao;
import com.jianshu.modules.frontBase.model.User;
import com.jianshu.modules.frontBase.model.vo.UserVo;
import com.jianshu.web.common.SessionUser;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
/**
* userService
*
* @author ritian
* @date 2018年4月14日
*/
@Service
public class ApiUserService extends BaseService {
@Resource
private UserDao userDao;
//根据openid 登录
public User login(String openid, String token) {
User user = this.findByOpenid(openid);
//user为空则保存至数据库中
if (user == null) {
//根据token及openid获取用户信息
JSONObject json = WXUtils.getUserInfo(token, openid);
if(json != null){
user = getUser(json);
user.setOpenid(openid);
this.save(user);
}
}
cacheUser(user);
return user;
}
public User get(int id){
return userDao.get(id);
}
/**
* 缓存用户信息,
*
* @param user 需要被缓存的用户
* @return 服务器缓存的用户信息
*/
private void cacheUser(User user) {
CacheUtils.put(Constants.Cache.USER_CACHE, user.getOpenid(), user);
}
public User findByOpenid(String openid) {
return userDao.findByOpenid(openid);
}
/**
* 保存
*/
public void save(User t) {
userDao.save(t);
}
private User getUser(JSONObject json) {
User user = new User();
user.setHeadImgUrl(json.optString("headimgurl"));
user.setNickname(json.getString("nickname"));
user.setCreateTime(new Date());
user.setProvince(json.optString("province"));
user.setCity(json.optString("city"));
user.setGender(json.optInt("sex"));
return user;
}
}
相关工具类,根据token及openid获取用户信息
参考微信官方文档一第四步:拉取用户信息(需scope为 snsapi_userinfo)
package com.ccamazing.common.utils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import com.github.pagehelper.util.StringUtil;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
/**
* 微信工具类(公众号开发)
*
* @author ritian
* @date 2018年4月14日
*/
public class WXUtils {
// 获取access_token的接口地址(GET) 限2000(次/天)
private final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
// 获取jsapi_ticket的接口地址(GET) 限2000(次/天)
private final static String jsapi_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
// 获取openId的接口地址
private final static String oauth_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
// 获取网页授权微信用户信息地址
private final static String sns_userinfo_url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESSTOKEN&openid=OPENID&lang=zh_CN";
// 获取微信卡券api_ticket 接口地址
private final static String api_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESSTOKEN&type=wx_card";
private static final Logger logger = LoggerFactory.getLogger(WXUtils.class);
/**
* 获取网页授权微信用户信息
*/
public static JSONObject getUserInfo(String token, String openId) {
try {
// 通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。
String url = sns_userinfo_url.replace("ACCESSTOKEN", token).replace(
"OPENID", openId);
String res = HttpUtils.doGet(url);
return JSONObject.fromObject(res);
} catch (Exception e) {
logger.error("用户授权获取用户信息失败!", e);
}
return null;
}
}
spring-mvc拦截器配置文件