尚硅谷在线教育十二:微信登录

文章目录

  • 1.OAuth2.0
  • 2. 微信扫描登录的准备
  • 3. 微信登录
    • 3.1 在service-ucenter模块配置文件 微信id,密钥和域名地址
    • 3.2创建读取配置文件的类
    • 3.3 生成微信扫描二维码
      • 3.3.1 获取微信二维码
      • 3.3.2 微信扫描之后的步骤
      • 3.3.3 微信登录功能完善

1.OAuth2.0

OAuth2.0是针对特定问题的一种解决方案,主要解决两个问题 1. 开放系统间授权 2. 分布式访问问题
尚硅谷在线教育十二:微信登录_第1张图片
尚硅谷在线教育十二:微信登录_第2张图片

2. 微信扫描登录的准备

  1. 注册开发中资质:支持企业类型,注册之后提供微信id和微信密钥
  2. 申请网站应用名称
  3. 需要域名地址(尚硅谷给我们准备好了)

3. 微信登录

3.1 在service-ucenter模块配置文件 微信id,密钥和域名地址

# 微信开放平台 appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台 appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
# 微信开放平台 重定向url
wx.open.redirect_url=http://localhost:8160/api/educenter/wx/callback

3.2创建读取配置文件的类

在service-enter下创建一个utils包,该包下创建一个ConstantWxUtils 用于微信登录相关的配置

@Component
public 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 WX_OPEN_APP_ID;
    public static String WX_OPEN_APP_SECRET;
    public static String WX_OPEN_REDIRECT_URL;

    @Override
    public void afterPropertiesSet() throws Exception {
     
        WX_OPEN_APP_ID = appId;
        WX_OPEN_APP_SECRET = appSecret;
        WX_OPEN_REDIRECT_URL = redirectUrl;
    }
}

3.3 生成微信扫描二维码

3.3.1 获取微信二维码

直接请求微信提供固定的地址,向地址后面拼接参数
controller

@CrossOrigin
@Controller
@RequestMapping("/api/educenter/wx")
public class WxApiController {
     

    //生成微信登录二维码
    @GetMapping("/login")
    public String getCode()
    {
     
        // 微信开放平台授权baseUrl  %s表示?占位符
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
        "?appid=%s" +
        "&redirect_uri=%s" +
        "&response_type=code" +
        "&scope=snsapi_login"+
        "&state=%s" +
        "#wechat_redirect";
        //对redirect_url进行URLEncode编码
        String redirectUrl= ConstantWxUtils.WX_OPEN_REDIRECT_URL;
        String encode=null;
        try {
     
             encode= URLEncoder.encode(redirectUrl, "utf-8");
        } catch (UnsupportedEncodingException e) {
     
            e.printStackTrace();
        }
        //设置%s的值
        String s = String.format(
                baseUrl,
                ConstantWxUtils.WX_OPEN_APP_ID,
                encode,
                "atguigu"
        );
        //重定向请求微信地址
        return "redirect:"+s;

    }
}    

微信扫描后的地址为:http://localhost:8150/api/educenter/wx/callback
我们需要在域名地址里面写程序,做出处理1. 把本地端口改为8160 2. 回调接口地址和域名跳转地址写成一样

3.3.2 微信扫描之后的步骤

  1. 扫描之后,执行本地的callback方法,在fallback获取两个值,在跳转的时候传递过来
  • state:原样传递
  • code:类似手机验证码,随机唯一值
  1. 拿着第一步的获取到的code值,请求微信提供的固定地址,获取到两个值
  • access_token:访问凭证
  • openid:每个微信唯一标识
  1. 拿着第二步获取的两个值access_toekn和openid,在去请求微信提供的一个固定地址,最终获取到微信扫描人的信息

用到的技术点:httpClient,Json转换工具(fast json 、gson)
代码实现
创建一个Http请求的工具类

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);
            }
            // 设置参数
            RequestConfig.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 = 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 = 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 = 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() {
     
                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;
        }
    }
 }   

微信登录接口的实现

@CrossOrigin
@Controller
@RequestMapping("/api/educenter/wx")
public class WxApiController {
     

    @Autowired
    private UcenterMemberService memberService;
   //获取扫描信息,添加数据
    @GetMapping("callback")
    public String callback(String code,String state) {
     
        try{
     
        System.out.println("code" + code);
        System.out.println("state" + state);

        //1. 向认证服务器发送请求换取access_token
        String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
                "?appid=%s" +
                "&secret=%s" +
                "&code=%s" +
                "&grant_type=authorization_code";
        String accessTokenUrl = String.format(baseAccessTokenUrl,
                ConstantWxUtils.WX_OPEN_APP_ID,
                ConstantWxUtils.WX_OPEN_APP_SECRET,
                code);
        //2.请求这个拼接好的地址,得到两个返回的值 access——token和openid
        //使用HttpClient发送请求得到返回结果
        String s = HttpClientUtils.get(accessTokenUrl);
            System.out.println(s);
        //从字符串s中获取出两个值 openid access_token
        //把字符串转换成map集合,根据map里面的key获取对应的值
        //使用json转换工具 gson
            Gson gson = new Gson();
            HashMap hashMap = gson.fromJson(s, HashMap.class);
            String openid = (String)hashMap.get("openid");
            String access_token =(String) hashMap.get("access_token");

            //把扫码人信息添加到数据库
            //根据openid判断是否存在相同微信的信息
            UcenterMember member= memberService.getOpenIdMember(openid);
            if(member==null)//member为空,表示没有相同微信的数据 进行添加
            {
     
                //3.拿着=access_token和openId在去请求微信提供的固定地址获取扫码人的信息
                //访问微信的资源服务器,获取用户信息
                String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                        "?access_token=%s" +
                        "&openid=%s";
                //拼接两个参数
                String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);
                //发送请求
                String userInfo = HttpClientUtils.get(userInfoUrl);
                System.out.println(userInfo);
                HashMap userMap = gson.fromJson(userInfo, HashMap.class);
                String nickname =(String) userMap.get("nickname");
                String headImgurl =(String) userMap.get("headimgurl");

              member=new UcenterMember();
              member.setOpenid(openid);
              member.setNickname(nickname);
              member.setAvatar(headImgurl);
              memberService.save(member);
            }


        }
        catch (Exception e)
        {
     
            throw new GuliException(20001,"登录失败");
        }
        finally {
     
            return "redirect:http://localhost:3000";
        }

    }

service判断openid是否存在

 //根据openid判断是否存在
    @Override
    public UcenterMember getOpenIdMember(String openid) {
     
        QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
        wrapper.eq("openid",openid);
        UcenterMember member = baseMapper.selectOne(wrapper);
        return member;
    }

3.3.3 微信登录功能完善

之前登录之后,在首页面从cookie获取数据进行显示,现在也可以按照这种方式,把扫描之后的信息放在cookie里面,跳到首页面进行显示,但cookie无法实现跨域访问
最终解决方案:根据微信信息实验jwt,生成字符串,把token字符串通过路径传递到首页面
在这里插入图片描述

在layout文件下的default.vue中加入路由地址获取token,调用用户的信息,
尚硅谷在线教育十二:微信登录_第3张图片

你可能感兴趣的:(java,微信,小程序,java)