微信网页授权登录且实现access_token分布式管理

建议:一定要先看官方文档

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

注意事项和说明官方文档都有说明,我就不说了

两种方案:第一种原生代码,我这里说第二种sdk实现,github上面有很多优秀的sdk,这里只说其一

github sdk地址:  https://github.com/Wechat-Group/weixin-java-tools/wiki

这是之前的版本,后面改名为 WXJava 了,对于新版的sdk,下面的代码有些已经不适用了,可更具新版sdk自行更改代码

本人拥有丰富的微信公众号开发和小程序开发以及支付开发经验,最近有点时间就打算写写博客

我使用的环境是springboot,ieda

使用方法: 

1)第一步:

导包一一>maven或者gradle

maven:


   com.github.binarywang
   weixin-java-mp
   3.1.0

gradle:

compile 'com.github.binarywang:weixin-java-mp:3.1.0'

2)第二部: 配置sdk的config

配置文件

yml中

official:
  appId: wx1c0abaaa92fbeef9
  appSecret: 5bf7cb8732af714d8540bbc5641f2df4
#  host必须加http或https
  host: http://like.s1.natapp.cc

属性config:

package com.xiaoyu.tool.configs.property;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "official")
@Data
public class OfficialProperty {
    private String appId;
    private String appSecret;
    private String host;
}

configuration: 重写sdk方法,使用redis管理access_token,防止两小时内每调用一次sdk方法就刷新一次access_token,此方法在分布式中尤为重要且解决了微信接口调用次数的限制

package com.xiaoyu.tool.configs;

import com.xiaoyu.tool.configs.property.OfficialProperty;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

@Slf4j
@Configuration
@EnableConfigurationProperties(OfficialProperty.class)
public class WeixinMpConfiguration {
    private final RedisTemplate redisTemplate;
    @Autowired
    private OfficialProperty officialProperty;

    public WeixinMpConfiguration(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    @Bean
    public WxMpService wxMpService(){
        WxMpSpringRedisConfigStorage configStorage = new WxMpSpringRedisConfigStorage(redisTemplate);
        configStorage.setAppId(officialProperty.getAppId());
        configStorage.setSecret(officialProperty.getAppSecret());
        WxMpService wxMpService = new WxMpServiceImpl();
        wxMpService.setWxMpConfigStorage(configStorage);
        return wxMpService;
    }
}

configStorage:重写sdk底层storage

package com.xiaoyu.tool.configs;

import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;

public class WxMpSpringRedisConfigStorage extends WxMpInMemoryConfigStorage {

    private final static String ACCESS_TOKEN_KEY = "wechat_access_token_";

    private final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket_";

    private final static String CARDAPI_TICKET_KEY = "wechat_cardapi_ticket_";

    private final RedisTemplate redisTemplate;


    private String accessTokenKey;

    private String jsapiTicketKey;

    private String cardapiTicketKey;


    public WxMpSpringRedisConfigStorage(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void setAppId(String appId) {
        super.setAppId(appId);
//        定义redis中存储的名称
        this.accessTokenKey = ACCESS_TOKEN_KEY.concat(appId);
        this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(appId);
        this.cardapiTicketKey = CARDAPI_TICKET_KEY.concat(appId);
    }

    @Override
    public String getAccessToken() {
        return redisTemplate.opsForValue().get(this.accessTokenKey);
    }

    @Override
    public boolean isAccessTokenExpired() {
        return redisTemplate.getExpire(this.accessTokenKey) < 2;
    }

    @Override
    public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
        redisTemplate.opsForValue().set(this.accessTokenKey,accessToken,expiresInSeconds - 200, TimeUnit.SECONDS);
    }

    @Override
    public void expireAccessToken() {
        redisTemplate.expire(this.accessTokenKey, 0, TimeUnit.SECONDS);
    }

    @Override
    public String getJsapiTicket() {
        return redisTemplate.opsForValue().get(this.jsapiTicketKey);
    }

    @Override
    public boolean isJsapiTicketExpired() {
        return redisTemplate.getExpire(this.jsapiTicketKey) < 2;
    }

    @Override
    public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
        redisTemplate.opsForValue().set(this.jsapiTicketKey, jsapiTicket, expiresInSeconds - 200, TimeUnit.SECONDS);
    }

    @Override
    public void expireJsapiTicket() {
        redisTemplate.expire(this.jsapiTicketKey, 0, TimeUnit.SECONDS);
    }


    @Override
    public String getCardApiTicket() {
        return redisTemplate.opsForValue().get(this.cardapiTicketKey);
    }

    @Override
    public boolean isCardApiTicketExpired() {
        return redisTemplate.getExpire(this.cardapiTicketKey) < 2;
    }

    @Override
    public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) {
        redisTemplate.opsForValue().set(this.cardapiTicketKey, cardApiTicket, expiresInSeconds - 200, TimeUnit.SECONDS);
    }

    @Override
    public void expireCardApiTicket() {
        redisTemplate.expire(this.cardapiTicketKey, 0, TimeUnit.SECONDS);
    }

}

 

redis配置:

package com.xiaoyu.tool.configs;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@ComponentScan
@Configuration
public class RedisConfiguration {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

 

3)第三步:具体实现 controller代码

package com.xiaoyu.tool.controller.user;

import com.xiaoyu.tool.configs.property.OfficialProperty;
import com.xiaoyu.tool.entity.User.User;
import com.xiaoyu.tool.service.user.UserWebAuthorizedService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.net.URLEncoder;

/**
 * @Author:xiaoyu
 * @Created on 2018/9/17
 * @Description 公众号网页授权登录
 */
@Controller
@RequestMapping(value = "/webAuthorized")
@Slf4j
public class UserWebAuthorizedController {

    private final OfficialProperty officialProperty;

    private final WxMpService wxMpService;

    private final UserWebAuthorizedService userWebAuthorizedService;

    public UserWebAuthorizedController(OfficialProperty officialProperty, WxMpService wxMpService, UserWebAuthorizedService userWebAuthorizedService) {
        this.officialProperty = officialProperty;
        this.wxMpService = wxMpService;
        this.userWebAuthorizedService = userWebAuthorizedService;
    }
    /**
    *sdk 简单便捷
    */
    @ApiOperation("微信网页授权")
    @GetMapping("/authorize")
    public String welcome(){
        //授权成功后跳转同域名下的网页
        String returnUrl = officialProperty.getHost() + "/index";
        //重定向的回调链接地址
        String url = officialProperty.getHost() + "/wechat_alipay_tool/webAuthorized/oauth2";
        String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_USERINFO, URLEncoder.encode(returnUrl));
        return "redirect:" + redirectUrl;
    }

    @ApiOperation("授权获取用户信息")
    @GetMapping("/oauth2")
    public String oauth2(@RequestParam(name = "code") String code,
                         @RequestParam("state") String returnUrl) {
        try {
            WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
            WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(accessToken, "zh_CN");
            User user = userWebAuthorizedService.createAndUpdateUser(wxMpUser);
            log.info("用户信息:{}",user);
        } catch (WxErrorException e) {
            log.error("通过code获取accessToken失败/获取用户信息失败:{}",e.getMessage());
        }
        return "redirect:" + returnUrl;
    }

}


测试环境: 申请微信测试号,配置natapp域名穿透

微信测试号登录: 

https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login&token=1361182780&lang=zh_CN

配置:

微信网页授权登录且实现access_token分布式管理_第1张图片

微信网页授权登录且实现access_token分布式管理_第2张图片

微信网页授权登录且实现access_token分布式管理_第3张图片

这几个不要配置错了,根据自己的域名配置

natapp:

https://natapp.cn/

 

你可能感兴趣的:(java,微信公众号,springboot)