1. 程式人生 > 實用技巧 >微信登入4-開發回撥URL

微信登入4-開發回撥URL

一、準備

1、引入pom依賴

在要使用HttpClient的專案中加入依賴

<!--httpclient-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

2、新增httpclient工具類

把HttpClientUtils.java放入專案的util包

/**
 * http請求客戶端
 */
public class HttpClientUtils {
	private String url;
	private Map<String, String> param;
	private int statusCode;
	private String content;
	private String xmlParam;
	private boolean isHttps;

	public boolean isHttps() {
		return isHttps;
	}

	public void setHttps(boolean isHttps) {
		this.isHttps = isHttps;
	}

	public String getXmlParam() {
		return xmlParam;
	}

	public void setXmlParam(String xmlParam) {
		this.xmlParam = xmlParam;
	}

	public HttpClientUtils(String url, Map<String, String> param) {
		this.url = url;
		this.param = param;
	}

	public HttpClientUtils(String url) {
		this.url = url;
	}

	public void setParameter(Map<String, String> map) {
		param = map;
	}

	public void addParameter(String key, String value) {
		if (param == null)
			param = new HashMap<String, String>();
		param.put(key, value);
	}

	public void post() throws ClientProtocolException, IOException {
		HttpPost http = new HttpPost(url);
		setEntity(http);
		execute(http);
	}

	public void put() throws ClientProtocolException, IOException {
		HttpPut http = new HttpPut(url);
		setEntity(http);
		execute(http);
	}

	public void get() throws ClientProtocolException, IOException {
		if (param != null) {
			StringBuilder url = new StringBuilder(this.url);
			boolean isFirst = true;
			for (String key : param.keySet()) {
				if (isFirst) {
					url.append("?");
					isFirst = false;
				}else {
					url.append("&");
				}
				url.append(key).append("=").append(param.get(key));
			}
			this.url = url.toString();
		}
		HttpGet http = new HttpGet(url);
		execute(http);
	}

	/**
	 * set http post,put param
	 */
	private void setEntity(HttpEntityEnclosingRequestBase http) {
		if (param != null) {
			List<NameValuePair> nvps = new LinkedList<NameValuePair>();
			for (String key : param.keySet())
				nvps.add(new BasicNameValuePair(key, param.get(key))); // 引數
			http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 設定引數
		}
		if (xmlParam != null) {
			http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
		}
	}

	private void execute(HttpUriRequest http) throws ClientProtocolException,
			IOException {
		CloseableHttpClient httpClient = null;
		try {
			if (isHttps) {
				SSLContext sslContext = new SSLContextBuilder()
						.loadTrustMaterial(null, new TrustStrategy() {
							// 信任所有
							public boolean isTrusted(X509Certificate[] chain,
									String authType)
									throws CertificateException {
								return true;
							}
						}).build();
				SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
						sslContext);
				httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
						.build();
			} else {
				httpClient = HttpClients.createDefault();
			}
			CloseableHttpResponse response = httpClient.execute(http);
			try {
				if (response != null) {
					if (response.getStatusLine() != null)
						statusCode = response.getStatusLine().getStatusCode();
					HttpEntity entity = response.getEntity();
					// 響應內容
					content = EntityUtils.toString(entity, Consts.UTF_8);
				}
			} finally {
				response.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			httpClient.close();
		}
	}

	public int getStatusCode() {
		return statusCode;
	}

	public String getContent() throws ParseException, IOException {
		return content;
	}

}

二、獲取access_token

在ApWxiController.java中新增如下方法

    @GetMapping("callback")
    public String callback(String code, String state, HttpSession session){
        System.out.println("callback被呼叫");
        System.out.println("code:" + code);
        System.out.println("state:" + state);

        if(StringUtils.isEmpty(code) || StringUtils.isEmpty(state)){
            log.error("非法回撥請求");
            throw new GuliException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }

        String sessionState = (String)session.getAttribute("wx_open_state");
        if(!state.equals(sessionState)){
            log.error("非法回撥請求");
            throw new GuliException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }


        //攜帶code臨時票據,和appid以及appsecret請求access_token和openid(微信唯一標識)
        String accessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
        //組裝引數:?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        Map<String, String> accessTokenParam = new HashMap<>();
        accessTokenParam.put("appid", ucenterProperties.getAppId());
        accessTokenParam.put("secret", ucenterProperties.getAppSecret());
        accessTokenParam.put("code", code);
        accessTokenParam.put("grant_type", "authorization_code");
        HttpClientUtils client = new HttpClientUtils(accessTokenUrl, accessTokenParam);

        String result = "";
        try {
            //傳送請求:組裝完整的url字串、傳送請求
            client.get();
            //得到響應
            result = client.getContent();
            System.out.println("result = " + result);
        } catch (Exception e) {
            log.error("獲取access_token失敗");
            throw new GuliException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }

        Gson gson = new Gson();
        HashMap<String, Object> resultMap = gson.fromJson(result, HashMap.class);

        //失敗的響應結果
        Object errcodeObj = resultMap.get("errcode");
        if(errcodeObj != null){
            Double errcode = (Double)errcodeObj;
            String errmsg = (String)resultMap.get("errmsg");
            log.error("獲取access_token失敗:" + "code:" + errcode + ", message:" +  errmsg);
            throw new GuliException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }

        //解析出結果中的access_token和openid
        String accessToken = (String)resultMap.get("access_token");
        String openid = (String)resultMap.get("openid");

        System.out.println("accessToken:" + accessToken);
        System.out.println("openid:" + openid);

        //在本地資料庫中查詢當前微信使用者的資訊
        Member member = memberService.getByOpenid(openid);

       //TODO
        
        return null;
    }

三、獲取使用者資訊

1、根據openid查詢使用者是否已註冊

業務介面:MemberService.java

/**
     * 根據openid返回使用者資訊
     * @param openid
     * @return
     */
Member getByOpenid(String openid);

業務實現:MemberServiceImpl.java

@Override
public Member getByOpenid(String openid) {
    QueryWrapper<Member> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("openid", openid);
    return baseMapper.selectOne(queryWrapper);
}

2、根據access_token獲取使用者資訊

@Autowired
private MemberService memberService;

@GetMapping("callback")
public String callback(String code, String state, HttpSession session){
			...
       //根據access_token獲取微信使用者的基本資訊
        //根據openid查詢當前使用者是否已經使用微信登入過該系統
        Member member = memberService.getByOpenid(openid);
        if(member == null){
    
            //向微信的資源伺服器發起請求,獲取當前使用者的使用者資訊
            String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo";
            Map<String, String> baseUserInfoParam = new HashMap();
            baseUserInfoParam.put("access_token", accessToken);
            baseUserInfoParam.put("openid", openid);
            client = new HttpClientUtils(baseUserInfoUrl, baseUserInfoParam);
    
            String resultUserInfo = null;
            try {
                client.get();
                resultUserInfo = client.getContent();
            } catch (Exception e) {
                log.error(ExceptionUtils.getMessage(e));
                throw new GuliException(ResultCodeEnum.FETCH_USERINFO_ERROR);
            }
    
            HashMap<String, Object> resultUserInfoMap = gson.fromJson(resultUserInfo, HashMap.class);
            if(resultUserInfoMap.get("errcode") != null){
                log.error("獲取使用者資訊失敗" + ",message:" + resultMap.get("errmsg"));
                throw new GuliException(ResultCodeEnum.FETCH_USERINFO_ERROR);
            }
    
            String nickname = (String)resultUserInfoMap.get("nickname");
            String headimgurl = (String)resultUserInfoMap.get("headimgurl");
            Double sex = (Double)resultUserInfoMap.get("sex");
    
            //使用者註冊
            member = new Member();
            member.setOpenid(openid);
            member.setNickname(nickname);
            member.setAvatar(headimgurl);
            member.setSex(sex.intValue());
            memberService.save(member);
        }
    
        JwtInfo jwtInfo = new JwtInfo();
        jwtInfo.setId(member.getId());
        jwtInfo.setNickname(member.getNickname());
        jwtInfo.setAvatar(member.getAvatar());
        String jwtToken = JwtUtils.getJwtToken(jwtInfo, 1800);
    
        //攜帶token跳轉
        return "redirect:http://localhost:3000?token=" + jwtToken;
}

四、前端整合

components/AppHeader.vue
mounted() {
    // 微信登入url token獲取
    this.token = this.$route.query.token
    if (this.token) {
        // 將token存在cookie中
        cookie.set('guli_jwt_token', this.token, { domain: 'localhost' })
        // 跳轉頁面:擦除url中的token
        // 注意:window物件在created方法中無法被訪問,因此要寫在mounted中
        window.location.href = '/'
    }
},