第一:微信公眾號測試平台
http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
http://393.104.170.232/chainStore/userController/pcAuth.do
第二:
只填ip地址,不要加http或https
第三:登錄頁面加入
<script type="text/javascript" src="web/pages/js/qrcode.js"></script>
<button onclick="wechatLogin()">微信登錄</button>
<input type="hidden" id="sessionId"/>
<div id="code"></div>
function checkLogin(){
console.log("checkLogin");
var i = 0;
var sessionId = $("#sessionId").val();
console.log("sessionId:"+sessionId);
if(sessionId!=""){
$.ajax({
url: "userController/polling.do",
type: "POST",
data:{sessionId:sessionId},
dataType:'json',
success: function (data) {
console.log(data);
if(data.status=="200"){
window.clearInterval(c);
//location.href = "/";
window.location = "web/main.jsp";
}else{
i++;
if(i>60){
window.clearInterval(c);
mini.alert("二維碼已失效!請刷新二維碼。");
}
}
}
});
}
}
function wechatLogin(){
$.ajax({
url: "userController/wxLoginPage.do",
type: "post",
dataType:'json',
success: function (data) {
$("#sessionId").val(data.sessionId);
var qrcode = new QRCode(document.getElementById("code"), {
width : 200,//設置寬高
height : 200
});
qrcode.makeCode(data.uri);
c = setInterval(checkLogin,5000);//輪詢查詢
}
});
}
第四:controller層
//pc點擊微信登錄,生成登錄二維碼
@ResponseBody
@RequestMapping("/wxLoginPage")
public Map<String,String> wxLoginPage(HttpServletRequest request) {
Map<String,String> map = new HashMap<String,String>();
try {
String sessionId = request.getSession().getId();
logger.info("sessionId:"+sessionId);
String uri = this.weChatAuthServiceImpl.getAuthorizationUrl("pc",sessionId);//設置redirect_uri和state=sessionId以及測試號信息,返回授權url
logger.info(uri);
map.put("sessionId", sessionId);
map.put("uri", uri);//用來前端生成二維碼
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
//掃描二維碼授權成功,取到code,回調方法
@RequestMapping("/pcAuth")
@ResponseBody
public String pcCallback(String code,String state,HttpServletRequest request,HttpServletResponse response) throws Exception {
logger.info("code:"+code+" = state:"+state);
String result = weChatAuthServiceImpl.getAccessToken(code);//根據code獲取access_token和openId,不懂看微信文檔
JSONObject jsonObject = JSONObject.parseObject(result);
//String refresh_token = jsonObject.getString("refresh_token");
String access_token = jsonObject.getString("access_token");
String openId = jsonObject.getString("openId");
logger.info("------------授權成功----------------");
JSONObject infoJson = weChatAuthServiceImpl.getUserInfo(access_token,openId);//根據token和openId獲取微信用戶信息,不懂看我上一篇文章開始分享的鏈接
if(infoJson!=null){
String nickname = infoJson.getString("nickName");
logger.info("-----nickname-----"+nickname);
logger.info("-----sessionId-----"+state);
infoJson.put("openId", openId);
//redisTemplate.opsForValue().set(state, infoJson, 10*60, TimeUnit.SECONDS);
this.userInfoService.saveWxUser(state,infoJson);
return "登錄成功!";
}
return "登錄失敗!";
}
//輪詢查詢key
@RequestMapping(value="/polling")
@ResponseBody
public Map<String, Object> polling(String sessionId,HttpServletRequest request,HttpServletResponse response){
Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
String infoJsonstr = this.userInfoService.getWxUser(sessionId);
if(infoJsonstr!=null){
JSONObject infoJson = JSON.parseObject(infoJsonstr);
this.userInfoService.delWxUser(sessionId);
String openId = (String)infoJson.get("openId");
//根據openId判斷我們網站是否存在該用戶,數據庫用戶表會保存用戶
SysUserInfo user = this.userInfoService.selectUserByWechat(openId);
if (user == null) {
String nickname = (String)infoJson.get("nickName");
String sex = (String)infoJson.get("sex");
SysUserInfo newuser = new SysUserInfo();
newuser.setCharSex(sex.equals("男")?"1":"0");
newuser.setCharUserName(openId);
newuser.setCharPassWord(openId);
newuser.setCharRealName(nickname);
String i = (String) userInfoService.save(newuser);//新增用戶
if(i.length()<1){
resultMap.put("status", 500);
resultMap.put("message", "登錄失敗:");
resultMap.put("success", false);
resultMap.put("msg", "登錄失敗!");
return resultMap;
}
}
//登錄操作
try {
UsernamePasswordToken token = new UsernamePasswordToken(openId, openId);//這里是用shiro登錄,反正該openId已經微信掃碼驗證
token.setRememberMe(true);
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
if(currentUser.isAuthenticated()){
resultMap.put("status", 200);
resultMap.put("message", "登錄成功");
resultMap.put("success", true);
resultMap.put("msg", "登錄成功");
}else{
resultMap.put("status", 500);
resultMap.put("message", "登錄失敗");
resultMap.put("success", false);
resultMap.put("msg", "登錄失敗");
}
//更新用戶最后登錄時間
/*Subject currentUser = SecurityUtils.getSubject();
SysUserInfo luser = (SysUserInfo) currentUser.getPrincipal();
SysUserInfo user1 = this.userInfoService.getById(luser.getId());
Map<String,String> map= this.userInfoService.getUserAgent(request);
user1.setCharLastIp(luser.getCharCurrIp());
user1.setCharCurrIp(request.getRemoteAddr());
user1.setDateModifyTime(new Date());
user1.setDateLoginTime(new Date());
user1.setCharBrowserType(map.get("type"));
user1.setCharBrowserVersion(map.get("version"));
user1.setDateLoginTime(new Date());
this.userInfoService.update(user1);*///更新用戶方法
} catch (Exception e) {
resultMap.put("message", "未知系統錯誤:" + e.getMessage());
}
return resultMap;
}else{//not has key
resultMap.put("status", 0);
return resultMap;
}
/*if(redisTemplate.hasKey(sessionId)){
JSONObject infoJson = (JSONObject)redisTemplate.opsForValue().get(sessionId);
redisTemplate.opsForValue().getOperations().delete(sessionId);
String openId = (String)infoJson.get("openId");
//根據openId判斷我們網站是否存在該用戶,數據庫用戶表會保存用戶
User user = userService.selectUserByWechat(openId);
if (user == null) {
String nickname = (String)infoJson.get("nickName");
String sex = (String)infoJson.get("sex");
User newuser = new User();
newuser.setSex(sex);
newuser.setWechat(openId);
newuser.setNickname(nickname);
int i = userService.insertUser(newuser);//新增用戶
if(i<1){
resultMap.put("status", 500);
resultMap.put("message", "登錄失敗:");
return resultMap;
}
}
//登錄操作
try {
UsernamePasswordToken token = new UsernamePasswordToken(openId, openId);//這里是用shiro登錄,反正該openId已經微信掃碼驗證
SecurityUtils.getSubject().login(token);
resultMap.put("status", 200);
resultMap.put("message", "登錄成功");
//更新用戶最后登錄時間
Subject currentUser = SecurityUtils.getSubject();
User luser = (User) currentUser.getPrincipal();
User user1 = new User();
user1.setId(luser.getId());
user1.setLastLogDate(new Date());
userService.updateUserByIdSelective(user1);//更新用戶方法
} catch (Exception e) {
resultMap.put("message", "未知系統錯誤:" + e.getMessage());
}
return resultMap;
}else{//not has key
resultMap.put("status", 0);
return resultMap;
}*/
}
第五servie
1:userService
@Override
public void saveWxUser(String state, JSONObject infoJson) {
logger.info("state:"+state+"==infoJson:"+infoJson);
SysUserInfo userInfo = new SysUserInfo();
userInfo.setCharUserCode(state);
userInfo.setCharUserName(infoJson.toJSONString());
this.save(userInfo);
}
@Override
public String getWxUser(String sessionId) {
List<Map<String,Object>> list = this.findBySql("select charUserName from SysUserInfo where charUserCode='"+sessionId+"'");
if(list!=null&&list.size()>0){
return list.get(0).get("CHARUSERNAME").toString();
}
return null;
}
@Override
public void delWxUser(String sessionId) {
this.executeSql("update SysUserInfo set charUserCode='"+sessionId+"已刪除' where charUserCode='"+sessionId+"'");
}
@Override
public SysUserInfo selectUserByWechat(String openId) {
List<SysUserInfo> list = this.find("from SysUserInfo where charUserName='"+openId+"'");
if(list!=null&&list.size()>0){
return list.get(0);
}
return null;
}
2:AuthService
public interface AuthService {
public abstract String getAccessToken(String code);
public abstract String getOpenId(String accessToken);
public abstract String refreshToken(String code);
public abstract String getAuthorizationUrl(String type,String state) throws UnsupportedEncodingException;
public abstract JSONObject getUserInfo(String accessToken,String openId);
}
3:DefaultAuthServiceImpl
public abstract class DefaultAuthServiceImpl implements AuthService{
public static RestTemplate getRestTemplate() {// 手動添加
SimpleClientHttpRequestFactory requestFactory=new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(120000);
List<HttpMessageConverter<?>> messageConverters = new LinkedList<>();
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
messageConverters.add(new MappingJackson2HttpMessageConverter());
RestTemplate restTemplate=new RestTemplate(messageConverters);
restTemplate.setRequestFactory(requestFactory);
return restTemplate;
}
}
4:WeChatAuthServiceImpl
@Service("weChatAuthServiceImpl")
public class WeChatAuthServiceImpl extends DefaultAuthServiceImpl implements WeChatAuthService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//請求此地址即跳轉到二維碼登錄界面
private static final String AUTHORIZATION_URL =
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect";
// 獲取用戶 openid 和access——toke 的 URL
private static final String ACCESSTOKE_OPENID_URL =
"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
private static final String REFRESH_TOKEN_URL =
"https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s";
private static final String USER_INFO_URL =
"https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";
private static final String APP_ID="wx58fa4136c0f";
private static final String APP_SECRET="127b28f355110eef97be9eae";
private static final String SCOPE = "snsapi_userinfo";//"snsapi_userinfo"; snsapi_login snsapi_userinfo snsapi_base
//private String pcCallbackUrl = "https://7dc6440a.ngrok.io//wechat/pcAuth"; //pc回調域名
private String pcCallbackUrl = "http://39.23.234.21/chainStore/userController/pcAuth.do"; //pc回調域名
private String mobileCallbackUrl = "https://7dc6440a.ngrok.io//wechat/mobileAuth"; //mobile回調域名
/**
* 第一步,帶着參數
* appid:公眾號的唯一標識
* redirect_uri:授權后重定向的回調鏈接地址
* response_type:返回類型,填寫code
* scope:應用授權作用域,snsapi_base / snsapi_userinfo
* state:非必傳,重定向后會帶上state參數,開發者可以填寫a-zA-Z0-9的參數值,最多128字節
* wechat_redirect:無論直接打開還是做頁面302重定向時候,必須帶此參數
* */
@Override
public String getAuthorizationUrl(String type,String state) throws UnsupportedEncodingException {
String callbackUrl = "";
Object urlState = "";
if("pc".equals(type)){//移動端 pc端回調方法不一樣
callbackUrl = URLEncoder.encode(pcCallbackUrl,"utf-8");
urlState = state;
}else if("mobile".equals(type)){
callbackUrl = URLEncoder.encode(mobileCallbackUrl,"utf-8");
urlState = System.currentTimeMillis();
}
String url = String.format(AUTHORIZATION_URL,APP_ID,callbackUrl,SCOPE,urlState);
return url;
}
/**
* 第二步
* 傳appid secret code grant_type=authorization_code
* 獲得 access_token openId等
* */
@Override
public String getAccessToken(String code) {
String url = String.format(ACCESSTOKE_OPENID_URL,APP_ID,APP_SECRET,code);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
URI uri = builder.build().encode().toUri();
String resp = getRestTemplate().getForObject(uri, String.class);
logger.error("getAccessToken resp = "+resp);
if(resp.contains("openid")){
JSONObject jsonObject = JSONObject.parseObject(resp);
String access_token = jsonObject.getString("access_token");
String openId = jsonObject.getString("openid");;
JSONObject res = new JSONObject();
res.put("access_token",access_token);
res.put("openId",openId);
res.put("refresh_token",jsonObject.getString("refresh_token"));
return res.toJSONString();
}else{
logger.error("獲取用戶信息錯誤,msg = "+resp);
return null;
}
}
//微信接口中,token和openId是一起返回,故此方法不需實現
@Override
public String getOpenId(String accessToken) {
return null;
}
@Override
public JSONObject getUserInfo(String accessToken, String openId){
String url = String.format(USER_INFO_URL, accessToken, openId);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
URI uri = builder.build().encode().toUri();
String resp = getRestTemplate().getForObject(uri, String.class);
logger.error("getUserInfo resp = "+resp);
if(resp.contains("errcode")){
logger.error("獲取用戶信息錯誤,msg = "+resp);
return null;
}else{
JSONObject data =JSONObject.parseObject(resp);
JSONObject result = new JSONObject();
result.put("id",data.getString("unionid"));
result.put("sex",data.getString("sex"));
result.put("nickName",data.getString("nickname"));
result.put("avatar",data.getString("headimgurl"));
return result;
}
}
//微信的token只有2小時的有效期,過時需要重新獲取,所以官方提供了
//根據refresh_token 刷新獲取token的方法,本項目僅僅是獲取用戶
//信息,並將信息存入庫,所以兩個小時也已經足夠了
@Override
public String refreshToken(String refresh_token) {
String url = String.format(REFRESH_TOKEN_URL,APP_ID,refresh_token);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
URI uri = builder.build().encode().toUri();
ResponseEntity<JSONObject> resp = getRestTemplate().getForEntity(uri,JSONObject.class);
JSONObject jsonObject = resp.getBody();
String access_token = jsonObject.getString("access_token");
return access_token;
}
}
5:WeChatAuthService
public interface WeChatAuthService {
public String getAuthorizationUrl(String string, String sessionId) throws UnsupportedEncodingException;
public String getAccessToken(String code);
public JSONObject getUserInfo(String accessToken, String openId);
}
需要三個jar包