zoukankan      html  css  js  c++  java
  • 使用SpringSocial开发微信登录

    ⒈编写微信用户对应的数据结构

      1 package cn.coreqi.social.weixin.entities;
      2 
      3 /**
      4  * 微信用户实体类
      5  */
      6 public class WeixinUserInfo {
      7     /**
      8      * 普通用户的标识,对当前开发者账号唯一
      9      */
     10     private String openid;
     11     /**
     12      * 普通用户昵称
     13      */
     14     private String nickname;
     15 
     16     /**
     17      * 语言
     18      */
     19     private String language;
     20 
     21     /**
     22      * 普通用户性别,1为男性,2为女性
     23      */
     24     private String sex;
     25     /**
     26      * 普通用户个人资料填写的省份
     27      */
     28     private String province;
     29     /**
     30      * 普通用户个人资料填写的城市
     31      */
     32     private String city;
     33     /**
     34      * 国家,如中国为CN
     35      */
     36     private String country;
     37     /**
     38      * 用户头像,最后一个数值代表正方形头像大小(有0,46,64,96,132数值可选,0代表640*640正方形)
     39      */
     40     private String headimgurl;
     41     /**
     42      * 用户特权信息,json数组,如微信沃卡用户为(chinaunicom)
     43      */
     44     private String[] privilege;
     45     /**
     46      * 用户统一标识,针对一个微信开放平台账号下的应用,同一用户的unionid是唯一的。
     47      */
     48     private String unionid;
     49 
     50 
     51     public String getOpenid() {
     52         return openid;
     53     }
     54 
     55     public void setOpenid(String openid) {
     56         this.openid = openid;
     57     }
     58 
     59     public String getNickname() {
     60         return nickname;
     61     }
     62 
     63     public void setNickname(String nickname) {
     64         this.nickname = nickname;
     65     }
     66 
     67     public String getLanguage() {
     68         return language;
     69     }
     70 
     71     public void setLanguage(String language) {
     72         this.language = language;
     73     }
     74 
     75     public String getSex() {
     76         return sex;
     77     }
     78 
     79     public void setSex(String sex) {
     80         this.sex = sex;
     81     }
     82 
     83     public String getProvince() {
     84         return province;
     85     }
     86 
     87     public void setProvince(String province) {
     88         this.province = province;
     89     }
     90 
     91     public String getCity() {
     92         return city;
     93     }
     94 
     95     public void setCity(String city) {
     96         this.city = city;
     97     }
     98 
     99     public String getCountry() {
    100         return country;
    101     }
    102 
    103     public void setCountry(String country) {
    104         this.country = country;
    105     }
    106 
    107     public String getHeadimgurl() {
    108         return headimgurl;
    109     }
    110 
    111     public void setHeadimgurl(String headimgurl) {
    112         this.headimgurl = headimgurl;
    113     }
    114 
    115     public String[] getPrivilege() {
    116         return privilege;
    117     }
    118 
    119     public void setPrivilege(String[] privilege) {
    120         this.privilege = privilege;
    121     }
    122 
    123     public String getUnionid() {
    124         return unionid;
    125     }
    126 
    127     public void setUnionid(String unionid) {
    128         this.unionid = unionid;
    129     }
    130 }

    ⒉编写一个微信API接口用于获取微信用户信息

     1 package cn.coreqi.social.weixin.api;
     2 
     3 
     4 import cn.coreqi.social.weixin.entities.WeixinUserInfo;
     5 
     6 /**
     7  * 微信API调用接口
     8  */
     9 public interface Weixin {
    10 
    11     /**
    12      * 获取微信用户信息
    13      * @param openId
    14      * @return
    15      */
    16     WeixinUserInfo getUserInfo(String openId);
    17 }

    ⒊编写一个微信API接口实现

     1 package cn.coreqi.social.weixin.api.impl;
     2 
     3 import cn.coreqi.social.weixin.api.Weixin;
     4 import cn.coreqi.social.weixin.entities.WeixinUserInfo;
     5 import com.fasterxml.jackson.databind.ObjectMapper;
     6 import org.apache.commons.lang.StringUtils;
     7 import org.springframework.http.converter.HttpMessageConverter;
     8 import org.springframework.http.converter.StringHttpMessageConverter;
     9 import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
    10 import org.springframework.social.oauth2.TokenStrategy;
    11 
    12 import java.nio.charset.Charset;
    13 import java.util.List;
    14 
    15 /**
    16  * 微信API调用模板,scope为Request的SpringBean,根据当前用户的accessToken创建。
    17  */
    18 public class WeixinImpl extends AbstractOAuth2ApiBinding implements Weixin {
    19 
    20     /**
    21      * 用于序列化Json数据
    22      */
    23     private ObjectMapper objectMapper = new ObjectMapper();
    24 
    25     /**
    26      * 获取用户信息url
    27      */
    28     private static final String URL_GET_USER_INFO="https://api.weixin.qq.com/sns/userinfo?openid=";
    29 
    30     /**
    31      * WeixinImpl构造器
    32      * @param accessToken
    33      */
    34     public WeixinImpl(String accessToken){
    35         super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
    36     }
    37 
    38     /**
    39      * 默认注册的HttpMessageConverter字符集为ISO-8859-1,而微信返回的是UTF-8,因此必须覆盖原来的方法
    40      * @return
    41      */
    42     @Override
    43     protected List<HttpMessageConverter<?>> getMessageConverters() {
    44         List<HttpMessageConverter<?>> messageConverters = super.getMessageConverters();
    45         messageConverters.remove(0);    //删除StringHttpMessageConverter
    46         messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
    47         return messageConverters;
    48     }
    49 
    50     /**
    51      * 获取微信用户信息
    52      * @param openId
    53      * @return
    54      */
    55     @Override
    56     public WeixinUserInfo getUserInfo(String openId) {
    57         String url = URL_GET_USER_INFO + openId;
    58         String response = getRestTemplate().getForObject(url,String.class);
    59         if(StringUtils.contains(response,"errcode")){   //如果响应中存在错误码则返回null
    60             return null;
    61         }
    62         WeixinUserInfo userInfo = null;
    63         try {
    64             userInfo = objectMapper.readValue(response,WeixinUserInfo.class);
    65         }catch (Exception e){
    66 
    67         }
    68         return userInfo;
    69     }
    70 }

    ⒋编写微信access_token类

     1 package cn.coreqi.social.weixin.connect;
     2 
     3 import org.springframework.social.oauth2.AccessGrant;
     4 
     5 /**
     6  * 对微信access_token信息的封装
     7  * 与标准的OAuth2协议不同,微信在获取access_token时会同时返回openId,并没有单独的通过accessToke换取openId的服务
     8  * 在此处继承标准AccessGrant(Spring提供的令牌封装类),添加openId字段
     9  */
    10 public class WeixinAccessGrant extends AccessGrant {
    11 
    12     private String openId;
    13 
    14     public WeixinAccessGrant() {
    15         super("");
    16     }
    17     public WeixinAccessGrant(String accessToken, String scope, String refreshToken, Long expiresIn) {
    18         super(accessToken, scope, refreshToken, expiresIn);
    19     }
    20 
    21     public String getOpenId() {
    22         return openId;
    23     }
    24 
    25     public void setOpenId(String openId) {
    26         this.openId = openId;
    27     }
    28 }

    ⒌编写微信OAuth2认证流程模板类。

      1 package cn.coreqi.social.weixin.connect;
      2 
      3 import java.nio.charset.Charset;
      4 import java.util.Map;
      5 import org.apache.commons.collections4.MapUtils;
      6 import org.apache.commons.lang.StringUtils;
      7 import org.slf4j.Logger;
      8 import org.slf4j.LoggerFactory;
      9 import org.springframework.http.converter.StringHttpMessageConverter;
     10 import org.springframework.social.oauth2.AccessGrant;
     11 import org.springframework.social.oauth2.OAuth2Parameters;
     12 import org.springframework.social.oauth2.OAuth2Template;
     13 import org.springframework.util.MultiValueMap;
     14 import org.springframework.web.client.RestTemplate;
     15 import com.fasterxml.jackson.databind.ObjectMapper;
     16 
     17 /**
     18  * 完成微信的OAuth2认证流程的模板类。
     19  * 国内厂商实现的OAuth2方式不同, Spring默认提供的OAuth2Template无法完整适配,只能针对每个厂商调整。
     20  */
     21 public class WeixinOAuth2Template extends OAuth2Template {
     22 
     23     private Logger logger = LoggerFactory.getLogger(getClass());
     24 
     25     private String clientId;
     26 
     27     private String clientSecret;
     28 
     29     private String accessTokenUrl;
     30 
     31     private static final String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token";
     32 
     33     public WeixinOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
     34         super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
     35         setUseParametersForClientAuthentication(true);  //请求中添加client_id和client_secret参数
     36         this.clientId = clientId;
     37         this.clientSecret = clientSecret;
     38         this.accessTokenUrl = accessTokenUrl;
     39     }
     40 
     41     /**
     42      * 微信变更了OAuth请求参数的名称,我们覆写相应的方法按照微信的文档改为微信请求参数的名字。
     43      * @param authorizationCode
     44      * @param redirectUri
     45      * @param parameters
     46      * @return
     47      */
     48     @Override
     49     public AccessGrant exchangeForAccess(String authorizationCode, String redirectUri,
     50                                          MultiValueMap<String, String> parameters) {
     51 
     52         StringBuilder accessTokenRequestUrl = new StringBuilder(accessTokenUrl);
     53 
     54         accessTokenRequestUrl.append("?appid="+clientId);
     55         accessTokenRequestUrl.append("&secret="+clientSecret);
     56         accessTokenRequestUrl.append("&code="+authorizationCode);
     57         accessTokenRequestUrl.append("&grant_type=authorization_code");
     58         accessTokenRequestUrl.append("&redirect_uri="+redirectUri);
     59 
     60         return getAccessToken(accessTokenRequestUrl);
     61     }
     62 
     63     /**
     64      * 微信变更了OAuth请求参数的名称,我们覆写相应的方法按照微信的文档改为微信请求参数的名字。
     65      * @param refreshToken
     66      * @param additionalParameters
     67      * @return
     68      */
     69     public AccessGrant refreshAccess(String refreshToken, MultiValueMap<String, String> additionalParameters) {
     70 
     71         StringBuilder refreshTokenUrl = new StringBuilder(REFRESH_TOKEN_URL);
     72 
     73         refreshTokenUrl.append("?appid="+clientId);
     74         refreshTokenUrl.append("&grant_type=refresh_token");
     75         refreshTokenUrl.append("&refresh_token="+refreshToken);
     76 
     77         return getAccessToken(refreshTokenUrl);
     78     }
     79 
     80     /**
     81      * 获取微信access_token信息
     82      * @param accessTokenRequestUrl
     83      * @return
     84      */
     85     @SuppressWarnings("unchecked")
     86     private AccessGrant getAccessToken(StringBuilder accessTokenRequestUrl) {
     87 
     88         logger.info("获取access_token, 请求URL: "+accessTokenRequestUrl.toString());
     89 
     90         String response = getRestTemplate().getForObject(accessTokenRequestUrl.toString(), String.class);
     91 
     92         logger.info("获取access_token, 响应内容: "+response);
     93 
     94         Map<String, Object> result = null;
     95         try {
     96             result = new ObjectMapper().readValue(response, Map.class);
     97         } catch (Exception e) {
     98             e.printStackTrace();
     99         }
    100 
    101         //返回错误码时直接返回空
    102         if(StringUtils.isNotBlank(MapUtils.getString(result, "errcode"))){
    103             String errcode = MapUtils.getString(result, "errcode");
    104             String errmsg = MapUtils.getString(result, "errmsg");
    105             throw new RuntimeException("获取access token失败, errcode:"+errcode+", errmsg:"+errmsg);
    106         }
    107 
    108         WeixinAccessGrant accessToken = new WeixinAccessGrant(
    109                 MapUtils.getString(result, "access_token"),
    110                 MapUtils.getString(result, "scope"),
    111                 MapUtils.getString(result, "refresh_token"),
    112                 MapUtils.getLong(result, "expires_in"));
    113 
    114         accessToken.setOpenId(MapUtils.getString(result, "openid"));
    115 
    116         return accessToken;
    117     }
    118 
    119     /**
    120      * 构建获取授权码的请求。也就是引导用户跳转到微信的地址。
    121      */
    122     public String buildAuthenticateUrl(OAuth2Parameters parameters) {
    123         String url = super.buildAuthenticateUrl(parameters);
    124         url = url + "&appid="+clientId+"&scope=snsapi_login";
    125         return url;
    126     }
    127 
    128     public String buildAuthorizeUrl(OAuth2Parameters parameters) {
    129         return buildAuthenticateUrl(parameters);
    130     }
    131 
    132     /**
    133      * 微信返回的contentType是html/text,添加相应的HttpMessageConverter来处理。
    134      */
    135     protected RestTemplate createRestTemplate() {
    136         RestTemplate restTemplate = super.createRestTemplate();
    137         restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
    138         return restTemplate;
    139     }
    140 
    141 }

    ⒍编写微信API适配器,将从微信API拿到的用户数据模型转换为Spring Social的标准用户数据模型。

     1 package cn.coreqi.social.weixin.connect;
     2 
     3 import cn.coreqi.social.weixin.api.Weixin;
     4 import cn.coreqi.social.weixin.entities.WeixinUserInfo;
     5 import org.springframework.social.connect.ApiAdapter;
     6 import org.springframework.social.connect.ConnectionValues;
     7 import org.springframework.social.connect.UserProfile;
     8 
     9 /**
    10  * 微信API适配器,将从微信API拿到的用户数据模型转换为Spring Social的标准用户数据模型。
    11  * @author fanqi 
    12  */
    13 public class WeixinAdapter implements ApiAdapter<Weixin> {
    14 
    15     private String openId;
    16 
    17     public WeixinAdapter() {}
    18 
    19     public WeixinAdapter(String openId){
    20         this.openId = openId;
    21     }
    22 
    23     /**
    24      * 用来测试当前的API是否可用
    25      * @param api
    26      * @return
    27      */
    28     @Override
    29     public boolean test(Weixin api) {
    30         return true;
    31     }
    32 
    33     /**
    34      * 将微信的用户信息映射到ConnectionValues标准的数据化结构上
    35      * @param api
    36      * @param values
    37      */
    38     @Override
    39     public void setConnectionValues(Weixin api, ConnectionValues values) {
    40         WeixinUserInfo profile = api.getUserInfo(openId);
    41         values.setProviderUserId(profile.getOpenid());
    42         values.setDisplayName(profile.getNickname());
    43         values.setImageUrl(profile.getHeadimgurl());
    44     }
    45 
    46     /**
    47      *
    48      * @param api
    49      * @return
    50      */
    51     @Override
    52     public UserProfile fetchUserProfile(Weixin api) {
    53         return null;
    54     }
    55 
    56     /**
    57      *
    58      * @param api
    59      * @param message
    60      */
    61     @Override
    62     public void updateStatus(Weixin api, String message) {
    63         //do nothing
    64     }
    65 
    66 }

    ⒎编写微信的OAuth2流程处理器的提供器

     1 package cn.coreqi.social.weixin.connect;
     2 
     3 import cn.coreqi.social.weixin.api.Weixin;
     4 import cn.coreqi.social.weixin.api.impl.WeixinImpl;
     5 import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider;
     6 
     7 /**
     8  * 微信的OAuth2流程处理器的提供器,供spring social的connect体系调用
     9  */
    10 public class WeixinServiceProvider extends AbstractOAuth2ServiceProvider<Weixin> {
    11 
    12     /**
    13      * 微信获取授权码的url
    14      */
    15     private static final String URL_AUTHORIZE = "https://open.weixin.qq.com/connect/qrconnect";
    16     /**
    17      * 微信获取accessToken的url
    18      */
    19     private static final String URL_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token";
    20 
    21     /**
    22      * 
    23      * @param appId
    24      * @param appSecret
    25      */
    26     public WeixinServiceProvider(String appId, String appSecret) {
    27         super(new WeixinOAuth2Template(appId, appSecret,URL_AUTHORIZE,URL_ACCESS_TOKEN));
    28     }
    29 
    30     /**
    31      * 
    32      * @param accessToken
    33      * @return
    34      */
    35     @Override
    36     public Weixin getApi(String accessToken) {
    37         return new WeixinImpl(accessToken);
    38     }
    39 
    40 }

    ⒏创建微信连接工厂

     1 package cn.coreqi.social.weixin.connect;
     2 
     3 import cn.coreqi.social.weixin.api.Weixin;
     4 import org.springframework.social.connect.ApiAdapter;
     5 import org.springframework.social.connect.Connection;
     6 import org.springframework.social.connect.ConnectionData;
     7 import org.springframework.social.connect.support.OAuth2Connection;
     8 import org.springframework.social.connect.support.OAuth2ConnectionFactory;
     9 import org.springframework.social.oauth2.AccessGrant;
    10 import org.springframework.social.oauth2.OAuth2ServiceProvider;
    11 
    12 /**
    13  * 微信连接工厂
    14  */
    15 public class WeixinConnectionFactory extends OAuth2ConnectionFactory<Weixin> {
    16 
    17     /**
    18      *
    19      * @param providerId
    20      * @param appId
    21      * @param appSecret
    22      */
    23     public WeixinConnectionFactory(String providerId, String appId, String appSecret) {
    24         super(providerId, new WeixinServiceProvider(appId, appSecret), new WeixinAdapter());
    25     }
    26 
    27     /**
    28      * 由于微信的openId是和accessToken一起返回的,所以在这里直接根据accessToken设置providerUserId即可,不用像QQ那样通过QQAdapter来获取
    29      * @param accessGrant
    30      * @return
    31      */
    32     @Override
    33     protected String extractProviderUserId(AccessGrant accessGrant) {
    34         if(accessGrant instanceof WeixinAccessGrant) {
    35             return ((WeixinAccessGrant)accessGrant).getOpenId();
    36         }
    37         return null;
    38     }
    39 
    40     /**
    41      *
    42      * @param accessGrant
    43      * @return
    44      */
    45     public Connection<Weixin> createConnection(AccessGrant accessGrant) {
    46         return new OAuth2Connection<Weixin>(getProviderId(), extractProviderUserId(accessGrant), accessGrant.getAccessToken(),
    47                 accessGrant.getRefreshToken(), accessGrant.getExpireTime(), getOAuth2ServiceProvider(), getApiAdapter(extractProviderUserId(accessGrant)));
    48     }
    49 
    50     /**
    51      *
    52      * @param data
    53      * @return
    54      */
    55     public Connection<Weixin> createConnection(ConnectionData data) {
    56         return new OAuth2Connection<Weixin>(data, getOAuth2ServiceProvider(), getApiAdapter(data.getProviderUserId()));
    57     }
    58 
    59     /**
    60      *
    61      * @param providerUserId
    62      * @return
    63      */
    64     private ApiAdapter<Weixin> getApiAdapter(String providerUserId) {
    65         return new WeixinAdapter(providerUserId);
    66     }
    67 
    68     /**
    69      *
    70      * @return
    71      */
    72     private OAuth2ServiceProvider<Weixin> getOAuth2ServiceProvider() {
    73         return (OAuth2ServiceProvider<Weixin>) getServiceProvider();
    74     }
    75 
    76 
    77 }

    ⒐创建微信登陆配置类

     1 package cn.coreqi.social.weixin.config;
     2 
     3 import cn.coreqi.social.weixin.connect.WeixinConnectionFactory;
     4 import org.springframework.boot.autoconfigure.social.SocialAutoConfigurerAdapter;
     5 import org.springframework.context.annotation.Configuration;
     6 import org.springframework.social.connect.ConnectionFactory;
     7 
     8 /**
     9  * 微信登录配置
    10  */
    11 @Configuration
    12 public class WeixinAutoConfiguration extends SocialAutoConfigurerAdapter {
    13 
    14     @Override
    15     protected ConnectionFactory<?> createConnectionFactory() {
    16         String providerId = "weixin";   //第三方id,用来决定发起第三方登录的url,默认是weixin
    17         String appId = "";
    18         String appSecret = "";
    19         return new WeixinConnectionFactory(providerId, appId,
    20                 appSecret);
    21     }
    22     
    23 }
  • 相关阅读:
    CSS3 transition 过度
    CSS3 2D转换
    jQuery 选择器
    bootstrap、qjuery、插件 、字体网页
    利用jquery.backstretch插件,背景切换
    js 背景自动切换
    Jquery
    分析动态网页请求爬取腾讯视频评论
    追女神助手v0.1
    动态加载网页的爬取总结
  • 原文地址:https://www.cnblogs.com/fanqisoft/p/10654207.html
Copyright © 2011-2022 走看看