SpringSocial開発微信ログイン

14548 ワード

この記事ではSpringBoot 2に基づいて説明します.0,SpringSocial開発微信登録.大部分は実はQQと登録してとても似ていて、原理は贅沢に述べなくて、みんなが先にSpringSocialを見てQQを開発して実戦編に登録することを許可することを提案します
1.Api開発
1.1ユーザ情報取得インタフェースの宣言
public interface Weixin {
    WeixinUserInfo getUserInfo(String openId);
}

1.2WeixinUserInfo
public class WeixinUserInfo {
    
    /**
     *        ,          
     */
    private String openid;  
    /**
     *       
     */
    private String nickname;
    /**
     *   
     */
    private String language;
    /**
     *       ,1   ,2   
     */
    private String sex;
    /**
     *              
     */
    private String province;
    /**
     *              
     */
    private String city;
    /**
     *   ,    CN
     */
    private String country;
    /**
     *     ,               ( 0、46、64、96、132    ,0  640*640     ),           
     */
    private String headimgurl;
    /**
     *       ,json  ,        (chinaunicom)
     */
    private String[] privilege;
    /**
     *       。                ,     unionid    。
     */
    private String unionid;

1.3ユーザ情報インタフェース実装クラスの取得
public class WeixinImpl extends AbstractOAuth2ApiBinding implements Weixin {
    
    /**
     * 
     */
    private ObjectMapper objectMapper = new ObjectMapper();
    /**
     *        url
     */
    private static final String URL_GET_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?openid=";
    
    /**
     * @param accessToken
     */
    public WeixinImpl(String accessToken) {
        super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
    }
    
    /**
     *      StringHttpMessageConverter    ISO-8859-1,       UTF-8 ,          。
     */
    protected List> getMessageConverters() {
        List> messageConverters = super.getMessageConverters();
        messageConverters.remove(0);
        messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
        return messageConverters;
    }

    /**
     *         。
     */
    @Override
    public WeixinUserInfo getUserInfo(String openId) {
        String url = URL_GET_USER_INFO + openId;
        String response = getRestTemplate().getForObject(url, String.class);
        if(StringUtils.contains(response, "errcode")) {
            return null;
        }
        WeixinUserInfo profile = null;
        try {
            profile = objectMapper.readValue(response, WeixinUserInfo.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return profile;
    }

}

OK、API部分の開発が完了し、QQとの違いが最も大きいのは、openIdを取得していないことです.そして、getUserInfoにはopenIdパラメータが1つ増えています.なぜopenIdを取得する必要がありませんか.WeChatがライセンスコードでtokenを入手してopenIdも持ち帰ったので、手順2 OAuthOperations開発2.1 WeixinOAuth 2 Templateを省いてくれました
public class WeixinOAuth2Template extends OAuth2Template {
    
    private String clientId;
    
    private String clientSecret;

    private String accessTokenUrl;
    
    private static final String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token";
    
    private Logger logger = LoggerFactory.getLogger(getClass());

    public WeixinOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
        super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
        setUseParametersForClientAuthentication(true);
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.accessTokenUrl = accessTokenUrl;
    }
    
    /* (non-Javadoc)
     * @see org.springframework.social.oauth2.OAuth2Template#exchangeForAccess(java.lang.String, java.lang.String, org.springframework.util.MultiValueMap)
     */
    @Override
    public AccessGrant exchangeForAccess(String authorizationCode, String redirectUri,
            MultiValueMap parameters) {
        
        StringBuilder accessTokenRequestUrl = new StringBuilder(accessTokenUrl);
        
        accessTokenRequestUrl.append("?appid="+clientId);
        accessTokenRequestUrl.append("&secret="+clientSecret);
        accessTokenRequestUrl.append("&code="+authorizationCode);
        accessTokenRequestUrl.append("&grant_type=authorization_code");
        accessTokenRequestUrl.append("&redirect_uri="+redirectUri);
        
        return getAccessToken(accessTokenRequestUrl);
    }
    
    public AccessGrant refreshAccess(String refreshToken, MultiValueMap additionalParameters) {
        
        StringBuilder refreshTokenUrl = new StringBuilder(REFRESH_TOKEN_URL);
        
        refreshTokenUrl.append("?appid="+clientId);
        refreshTokenUrl.append("&grant_type=refresh_token");
        refreshTokenUrl.append("&refresh_token="+refreshToken);
        
        return getAccessToken(refreshTokenUrl);
    }

    @SuppressWarnings("unchecked")
    private AccessGrant getAccessToken(StringBuilder accessTokenRequestUrl) {
        
        logger.info("  access_token,   URL: "+accessTokenRequestUrl.toString());
        
        String response = getRestTemplate().getForObject(accessTokenRequestUrl.toString(), String.class);
        
        logger.info("  access_token,     : "+response);
        
        Map result = null;
        try {
            result = new ObjectMapper().readValue(response, Map.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        //           
        if(StringUtils.isNotBlank(MapUtils.getString(result, "errcode"))){
            String errcode = MapUtils.getString(result, "errcode");
            String errmsg = MapUtils.getString(result, "errmsg");
            throw new RuntimeException("  access token  , errcode:"+errcode+", errmsg:"+errmsg);
        }
        
        WeixinAccessGrant accessToken = new WeixinAccessGrant(
                MapUtils.getString(result, "access_token"), 
                MapUtils.getString(result, "scope"), 
                MapUtils.getString(result, "refresh_token"), 
                MapUtils.getLong(result, "expires_in"));
        
        accessToken.setOpenId(MapUtils.getString(result, "openid"));
        
        return accessToken;
    }
    
    /**
     *           。               。
     */
    public String buildAuthenticateUrl(OAuth2Parameters parameters) {
        String url = super.buildAuthenticateUrl(parameters);
        url = url + "&appid="+clientId+"&scope=snsapi_login";
        return url;
    }
    
    public String buildAuthorizeUrl(OAuth2Parameters parameters) {
        return buildAuthenticateUrl(parameters);
    }
    
    /**
     *      contentType html/text,     HttpMessageConverter   。
     */
    protected RestTemplate createRestTemplate() {
        RestTemplate restTemplate = super.createRestTemplate();
        restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
        return restTemplate;
    }

}

2.2 WeixinAccessGrant
public class WeixinAccessGrant extends AccessGrant {
    
    /**
     * 
     */
    private static final long serialVersionUID = -7243374526633186782L;
    
    private String openId;
    
    public WeixinAccessGrant() {
        super("");
    }

    public WeixinAccessGrant(String accessToken, String scope, String refreshToken, Long expiresIn) {
        super(accessToken, scope, refreshToken, expiresIn);
    }

    /**
     * @return the openId
     */
    public String getOpenId() {
        return openId;
    }

    /**
     * @param openId the openId to set
     */
    public void setOpenId(String openId) {
        this.openId = openId;
    }
    
}

3 ServiceProvider
public class WeixinServiceProvider extends AbstractOAuth2ServiceProvider {
    
    /**
     *         url
     */
    private static final String URL_AUTHORIZE = "https://open.weixin.qq.com/connect/qrconnect";
    /**
     *     accessToken url
     */
    private static final String URL_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token";

    /**
     * @param appId
     * @param appSecret
     */
    public WeixinServiceProvider(String appId, String appSecret) {
        super(new WeixinOAuth2Template(appId, appSecret,URL_AUTHORIZE,URL_ACCESS_TOKEN));
    }


    /* (non-Javadoc)
     * @see org.springframework.social.oauth2.AbstractOAuth2ServiceProvider#getApi(java.lang.String)
     */
    @Override
    public Weixin getApi(String accessToken) {
        return new WeixinImpl(accessToken);
    }

}

4.ApiAdapter
public class WeixinAdapter implements ApiAdapter {
    
    private String openId;
    
    public WeixinAdapter() {}
    
    public WeixinAdapter(String openId){
        this.openId = openId;
    }

    /**
     * @param api
     * @return
     */
    @Override
    public boolean test(Weixin api) {
        return true;
    }

    /**
     * @param api
     * @param values
     */
    @Override
    public void setConnectionValues(Weixin api, ConnectionValues values) {
        WeixinUserInfo profile = api.getUserInfo(openId);
        values.setProviderUserId(profile.getOpenid());
        values.setDisplayName(profile.getNickname());
        values.setImageUrl(profile.getHeadimgurl());
    }

    /**
     * @param api
     * @return
     */
    @Override
    public UserProfile fetchUserProfile(Weixin api) {
        return null;
    }

    /**
     * @param api
     * @param message
     */
    @Override
    public void updateStatus(Weixin api, String message) {
        //do nothing
    }

}

5. OAuth2ConnectionFactory
public class WeixinConnectionFactory extends OAuth2ConnectionFactory {
    
    /**
     * @param appId
     * @param appSecret
     */
    public WeixinConnectionFactory(String providerId, String appId, String appSecret) {
        super(providerId, new WeixinServiceProvider(appId, appSecret), new WeixinAdapter());
    }
    
    /**
     *      openId  accessToken     ,         accessToken  providerUserId  ,   QQ    QQAdapter   
     */
    @Override
    protected String extractProviderUserId(AccessGrant accessGrant) {
        if(accessGrant instanceof WeixinAccessGrant) {
            return ((WeixinAccessGrant)accessGrant).getOpenId();
        }
        return null;
    }
    
    /* (non-Javadoc)
     * @see org.springframework.social.connect.support.OAuth2ConnectionFactory#createConnection(org.springframework.social.oauth2.AccessGrant)
     */
    public Connection createConnection(AccessGrant accessGrant) {
        return new OAuth2Connection(getProviderId(), extractProviderUserId(accessGrant), accessGrant.getAccessToken(),
                accessGrant.getRefreshToken(), accessGrant.getExpireTime(), getOAuth2ServiceProvider(), getApiAdapter(extractProviderUserId(accessGrant)));
    }

    /* (non-Javadoc)
     * @see org.springframework.social.connect.support.OAuth2ConnectionFactory#createConnection(org.springframework.social.connect.ConnectionData)
     */
    public Connection createConnection(ConnectionData data) {
        return new OAuth2Connection(data, getOAuth2ServiceProvider(), getApiAdapter(data.getProviderUserId()));
    }
    
    private ApiAdapter getApiAdapter(String providerUserId) {
        return new WeixinAdapter(providerUserId);
    }
    
    private OAuth2ServiceProvider getOAuth2ServiceProvider() {
        return (OAuth2ServiceProvider) getServiceProvider();
    }
}

6 SocialConfigの構成
@Configuration
@EnableSocial
@ConditionalOnProperty(prefix = "imooc.security.social.qq", name = "app-id")
public class SocialConfig extends SocialConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private SecurityProperties securityProperties;
    
    @Autowired(required = false)
    private ConnectionSignUp connectionSignUp;

    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource,
                connectionFactoryLocator, Encryptors.noOpText());
        repository.setTablePrefix("imooc_");
        if(connectionSignUp != null) {
            repository.setConnectionSignUp(connectionSignUp);
        }
        return repository;
    }

    @Bean
    public SpringSocialConfigurer imoocSocialSecurityConfig() {
        String filterProcessesUrl = securityProperties.getSocial().getFilterProcessesUrl();
        ImoocSpringSocialConfigurer configurer = new ImoocSpringSocialConfigurer(filterProcessesUrl);
        configurer.signupUrl(securityProperties.getBrowser().getSignUpUrl());
        return configurer;
    }

    @Bean
    public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator connectionFactoryLocator) {
        return new ProviderSignInUtils(connectionFactoryLocator,
                getUsersConnectionRepository(connectionFactoryLocator)) {
        };
    }
    
    @Override
    public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer,
            Environment environment) {
        super.addConnectionFactories(connectionFactoryConfigurer, environment);
        QQProperties qqConfig = securityProperties.getSocial().getQq();
        WeixinProperties weixinConfig = securityProperties.getSocial().getWeixin();
        connectionFactoryConfigurer.addConnectionFactory(
                new QQConnectionFactory(qqConfig.getProviderId(), qqConfig.getAppId(), qqConfig.getAppSecret()));
        connectionFactoryConfigurer.addConnectionFactory(new WeixinConnectionFactory(weixinConfig.getProviderId(),
                weixinConfig.getAppId(), weixinConfig.getAppSecret()));

    }
    
    @Override
    public UserIdSource getUserIdSource() {
        return new AuthenticationNameUserIdSource();
    }
}

微信の配置とQQの差は多くなく、ここではコードを貼り付けるだけで、具体的な疑問点は前の文章を参考にすることができます.