阅读 397

项目整合微信扫码登录(含代码!复制即用)

1、整体流程

来源于官方文档

img

2、添加配置

appid appsecret 需要去微信开发平台注册 open.weixin.qq.com/

我这里使用谷粒学院项目的

# 微信开放平台 appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台 appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
# 微信开放平台 重定向url你的网站
wx.open.redirect_url=http://guli.shop/api/ucenter/wx/callback
复制代码

3、创建常量类

@Componentpublic
class ConstantWxUtils implements InitializingBean { 
    @Value("${wx.open.app_id}")
    private String appId;
    @Value("${wx.open.app_secret}")   
    private String appSecret; 
    @Value("${wx.open.redirect_url}")   
    private String RedirectUrl;
    public static String APP_ID;
    public static String APP_SECRET;
    public static String REDIRECT_URL;
    @Override
    public void afterPropertiesSet() throws Exception {  
        APP_ID = appId; 
        APP_SECRET = appSecret; 
        REDIRECT_URL = RedirectUrl;  
    }
}
复制代码

4、生成微信二维码

流程位置

img

Service层

/**
  *生成微信二维码 
  */
public String getWxCode() {
    /*
    固定网址,参数作用  
    appid
    应用唯一标识   
    redirect_uri 
    请使用urlEncode对链接进行处理     
    response_type 
    填code 
    scope 
    应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login 
    state
    用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验     
    */ 
    String url = "https://open.weixin.qq.com/connect/qrconnect?" +   
        "appid=%s" + 
        "&redirect_uri=%s" + 
        "&response_type=code" +   
        "&scope=snsapi_login" +
        "&state=%s" +  
        "#wechat_redirect"; 
    String encodeRedirectUrl = ConstantWxUtils.REDIRECT_URL;
    try {      
        encodeRedirectUrl = URLEncoder.encode(encodeRedirectUrl, "UTF-8"); 
    } catch (UnsupportedEncodingException e) {      
        e.printStackTrace();  
    }
    url = String.format( 
        url, 
        ConstantWxUtils.APP_ID, 
        encodeRedirectUrl,  
        "Tcl_guli"  
    );
    return url;
}
复制代码

Controller层

@ApiOperation("生成微信二维码")
@GetMapping()
public String getWxCode() {
    String url = wxApiService.getWxCode(); 
    return "redirect:" + url;
}
复制代码

调用后成功会出现微信二维码

5、扫码登录获得用户信息

5.1 导入依赖

扫码登录获得用户信息过程中会向固定网址发送请求所以要使用httpclient发送请求(向浏览器发送请求一样,只不过这次是在代码中)

        <!--httpclient 发送请求-->     
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>            			                     <artifactId>httpclient</artifactId>       
        </dependency>  
        <!--commons-io 发送请求工具类需要的依赖-->     
         <dependency>  
            <groupId>commons-io</groupId>  
            <artifactId>commons-io</artifactId>    
        </dependency>  
        <!--gson  json转换工具-->     
            <dependency>
            <groupId>com.google.code.gson</groupId>            				             
            <artifactId>gson</artifactId>    
        </dependency>
复制代码

5.2 Httpclient工具类

直接用,不用看

package com.liang.edu.utils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
 * 依赖的jar包有:commons-lang-2.6.jar、httpclient-4.3.2.jar、httpcore-4.3.1.jar、commons-io-2.4.jar
 *
 * @author zhaoyb
 */
public class HttpClientUtils {
    public static final int connTimeout = 10000;
    public static final int readTimeout = 10000;
    public static final String charset = "UTF-8";
    private static HttpClient client = null;
    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(128);
        cm.setDefaultMaxPerRoute(128);
        client = HttpClients.custom().setConnectionManager(cm).build();
    }
    public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception {
        return post(url, parameterStr, "application/x-www-form-urlencoded", charset, connTimeout, readTimeout);
    }
    public static String postParameters(String url, String parameterStr, String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception {
        return post(url, parameterStr, "application/x-www-form-urlencoded", charset, connTimeout, readTimeout);
    }
    public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {
        return postForm(url, params, null, connTimeout, readTimeout);
    }
    public static String postParameters(String url, Map<String, String> params, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {
        return postForm(url, params, null, connTimeout, readTimeout);
    }
    public static String get(String url) throws Exception {
        return get(url, charset, null, null);
    }
    public static String get(String url, String charset) throws Exception {
        return get(url, charset, connTimeout, readTimeout);
    }
    /**
     * 发送一个 Post 请求, 使用指定的字符集编码.
     *
     * @param url
     * @param body        RequestBody
     * @param mimeType    例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
     * @param charset     编码
     * @param connTimeout 建立链接超时时间,毫秒.
     * @param readTimeout 响应超时时间,毫秒.
     * @return ResponseBody, 使用指定的字符集编码.
     * @throws ConnectTimeoutException 建立链接超时异常
     * @throws SocketTimeoutException  响应超时
     * @throws Exception
     */
    public static String post(String url, String body, String mimeType, String charset, Integer connTimeout, Integer readTimeout)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        String result = "";
        try {
            if (StringUtils.isNotBlank(body)) {
                HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
                post.setEntity(entity);
            }
            // 设置参数
            Builder customReqConf = RequestConfig.custom();
            if (connTimeout != null) {
                customReqConf.setConnectTimeout(connTimeout);
            }
            if (readTimeout != null) {
                customReqConf.setSocketTimeout(readTimeout);
            }
            post.setConfig(customReqConf.build());
            HttpResponse res;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = com.liang.edu.utils.HttpClientUtils.client;
                res = client.execute(post);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }
    /**
     * 提交form表单
     *
     * @param url
     * @param params
     * @param connTimeout
     * @param readTimeout
     * @return
     * @throws ConnectTimeoutException
     * @throws SocketTimeoutException
     * @throws Exception
     */
    public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        try {
            if (params != null && !params.isEmpty()) {
                List<NameValuePair> formParams = new ArrayList<NameValuePair>();
                Set<Entry<String, String>> entrySet = params.entrySet();
                for (Entry<String, String> entry : entrySet) {
                    formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
                post.setEntity(entity);
            }
            if (headers != null && !headers.isEmpty()) {
                for (Entry<String, String> entry : headers.entrySet()) {
                    post.addHeader(entry.getKey(), entry.getValue());
                }
            }
            // 设置参数
            Builder customReqConf = RequestConfig.custom();
            if (connTimeout != null) {
                customReqConf.setConnectTimeout(connTimeout);
            }
            if (readTimeout != null) {
                customReqConf.setSocketTimeout(readTimeout);
            }
            post.setConfig(customReqConf.build());
            HttpResponse res = null;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = com.liang.edu.utils.HttpClientUtils.client;
                res = client.execute(post);
            }
            return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
    }
    /**
     * 发送一个 GET 请求
     *
     * @param url
     * @param charset
     * @param connTimeout 建立链接超时时间,毫秒.
     * @param readTimeout 响应超时时间,毫秒.
     * @return
     * @throws ConnectTimeoutException 建立链接超时
     * @throws SocketTimeoutException  响应超时
     * @throws Exception
     */
    public static String get(String url, String charset, Integer connTimeout, Integer readTimeout)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpGet get = new HttpGet(url);
        String result = "";
        try {
            // 设置参数
            Builder customReqConf = RequestConfig.custom();
            if (connTimeout != null) {
                customReqConf.setConnectTimeout(connTimeout);
            }
            if (readTimeout != null) {
                customReqConf.setSocketTimeout(readTimeout);
            }
            get.setConfig(customReqConf.build());
            HttpResponse res = null;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(get);
            } else {
                // 执行 Http 请求.
                client = com.liang.edu.utils.HttpClientUtils.client;
                res = client.execute(get);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            get.releaseConnection();
            if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }
    /**
     * 从 response 里获取 charset
     *
     * @param ressponse
     * @return
     */
    @SuppressWarnings("unused")
    private static String getCharsetFromResponse(HttpResponse ressponse) {
        // Content-Type:text/html; charset=GBK
        if (ressponse.getEntity() != null && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
            String contentType = ressponse.getEntity().getContentType().getValue();
            if (contentType.contains("charset=")) {
                return contentType.substring(contentType.indexOf("charset=") + 8);
            }
        }
        return null;
    }
    /**
     * 创建 SSL连接
     *
     * @return
     * @throws GeneralSecurityException
     */
    private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }
                @Override
                public void verify(String host, SSLSocket ssl)
                        throws IOException {
                }
                @Override
                public void verify(String host, X509Certificate cert)
                        throws SSLException {
                }
                @Override
                public void verify(String host, String[] cns,
                                   String[] subjectAlts) throws SSLException {
                }
            });
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (GeneralSecurityException e) {
            throw e;
        }
    }
    public static void main(String[] args) {
        try {
            String str = post("https://localhost:443/ssl/test.shtml", "name=12&page=34", "application/x-www-form-urlencoded", "UTF-8", 10000, 10000);
            //String str= get("https://localhost:443/ssl/test.shtml?name=12&page=34","GBK");
            /*Map<String,String> map = new HashMap<String,String>();
            map.put("name", "111");
            map.put("page", "222");
            String str= postForm("https://localhost:443/ssl/test.shtml",map,null, 10000, 10000);*/
            System.out.println(str);
        } catch (ConnectTimeoutException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SocketTimeoutException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

复制代码

5.3 所在流程位置

img

Controller层

在生成微信二维码中设置的重定向url就是来调用该方法,参数code,status都是成功扫描二维码,确认授权后获得的参数

@ApiOperation("获得扫码人信息")
@GetMapping("callback")
public String callback(String code,String status){   
    String token = wxApiService.callback(code,status); 
    return "redirect:http://localhost:3000?token=" + token;
}
复制代码

Service层

/** 
  *获得扫码用户信息 
  * 
  * @param code 扫码成功生成的code 
  * @param status 
  * @return 
  */
public String callback(String code, String status) {
    String token = "";    
    try { 
        //通过code获取access_token  访问固定网站 
        //参数说明
        /*
        appid:应用唯一标识,在微信开放平台提交应用审核通过后获得   
        secret:应用密钥AppSecret,在微信开放平台提交应用审核通过后获得   
        code:填写第一步(扫描二维码登录成功返回的code)获取的code参数   
        grant_type:填authorization_code      
        * */
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +                	                    "appid=%s" +     
                     "&secret=%s" +
            		 "&code=%s" +
            		 "&grant_type=authorization_code";
        //填写参数 
        url = String.format(url, ConstantWxUtils.APP_ID, ConstantWxUtils.APP_SECRET, code);        
        //使用工具类发送请求     
        String accessTokenInfo = HttpClientUtils.get(url);       
        /*        
        accessTokenInfo结果集: 
        access_token   接口调用凭证      
        expires_in    
        access_token接口调用凭证超时时间,单位(秒)    
        refresh_token  用户刷新access_token 
        openid
        授权用户唯一标识   
        scope  
        用户授权的作用域,使用逗号(,)分隔     
        unionid 
        当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段。  
        * */ 
        //使用json转换工具将结果集转换为map        
        Gson gson = new Gson(); 
        HashMap<String, String> map = gson.fromJson(accessTokenInfo,                HashMap.class);     
        String accessToken = map.get("access_token");       
        String openid = map.get("openid");  
        QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();        		 	 
        wrapper.eq("openid", openid); 
        UcenterMember member = memberService.getOne(wrapper);   
        //根据openid查询记录 查看是否有该用户微信登录的信息 若没有就记录  
        if (member == null) { 
            //通过access_token调用接口获取用户个人信息 (前提:1. access_token有效且未超时 2. 微信用户已授权给第三方应用帐号相应接口作用域(scope)。)       
            String wxUrl = "https://api.weixin.qq.com/sns/userinfo?" +                                              "access_token=%s" + 
                		   "&openid=%s"; 
            wxUrl = String.format(wxUrl, accessToken, openid);   
            String memberInfo = HttpClientUtils.get(wxUrl);    
            /*      
            openid 普通用户的标识,对当前开发者帐号唯一    
            nickname   普通用户昵称       
            sex    普通用户性别,1为男性,2为女性       
            province   普通用户个人资料填写的省份     
            city   普通用户个人资料填写的城市     
            country    国家,如中国为CN     
            headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空 
            privilege  用户特权信息,json数组,如微信沃卡用户为(chinaunicom)            			        unionid    用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。               */
            HashMap<String, String> infoMap = gson.fromJson(memberInfo, HashMap.class);            	  String nickname = infoMap.get("nickname");   
            String headimgurl = infoMap.get("headimgurl"); 
            member = new UcenterMember();  
            member.setOpenid(openid);  
            member.setNickname(nickname);     
            member.setAvatar(headimgurl);  
            memberService.save(member);    
        } 
        token = JwtUtils.getJwtToken(member.getId(), member.getNickname());    } catch (Exception e) {
        e.printStackTrace(); 
        throw new ServerException("登陆失败");    
    } 
    return token;
}
@ApiOperation("获得扫码人信息")
@GetMapping("callback")
public String callback(String code,String status){
    String token = wxApiService.callback(code,status);  
    return "redirect:http://localhost:3000?token=" + token;
}
复制代码

6、测试

12.png

可以看到我们频接了对应的url,拿到这个url后,我们在打开一个新的页面去访问,就会生成二位码,只要你的appid和和回调地址以及appsecret是真实的,就能出现二位码,如果不存在,会提示你appid参数错误等信息

13.png

完成!

文章分类
后端
文章标签