微信公众号开发教程第19篇——使用Spring+Redis方式保存access_token

使用Spring+Redis方式保存access_token长期有效

除了使用properties和servlet方式之外还有好的方法,希望读者朋友留言分享经验。

示例使用SpringMVC+Redis方式,逻辑流程适用于memcached,这里用到了spring-data-redis-1.7.5.RELEASE.jar

Spring的配置省略,并非本节重点,主要开启注解配置,并在其Spring-context.xml中引用到spring-redis.xml

<!-- 引入同文件夹下的redis属性配置 -->
<import resource="spring-redis.xml"/>

以下就是redis配置,并设置AccessToken的源码了

redis.properties

redis.host=127.0.0.1
redis.port=6379
redis.pass=ibloger.net_redis

redis.maxIdle=200
redis.maxTotal=512
redis.maxWaitMillis=3000
redis.testOnBorrow=true

spring-redis.xml配置

<!-- 扫描redis.properties文件-->
<context:property-placeholder location="classpath:config/redis.properties" />

<!-- 读取redis开始 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxTotal" value="${redis.maxTotal}" />         <!--最大分配的对象数 -->
    <property name="maxIdle" value="${redis.maxIdle}" />           <!--最大能够保持idel空闲状态的对象数 -->
    <property name="maxWaitMillis" value="${redis.maxWaitMillis}" /> <!--最大等待时间ms -->
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />   <!-- 当调用borrow Object方法时,在获取连接的时候是否进行检查有效性 -->
</bean>

<!-- redis连接工厂类 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
    p:host-name="${redis.host}" p:port="${redis.port}" p:pool-config-ref="jedisPoolConfig" />

<!-- redis模板类 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory" />

RedisBaseDao基类

package net.ibloger.wechat.redis.dao;

/**
 * 设置一个Redis基类,根据泛型+反射整理,实现通用方法
 * <br>
 * keyId代表Class类名全路径 + "_" + keyId  例如:key为id,那么到实现类中,就是 net.ibloger.demo.User_id
 * <br>
 * 两个方法没有提供
 *   一:没有提供修改方法,set中key相同替换相当于更新
 *   二:没有提供添加集合方法,因为 T 不能确定某一个参数值,所以集合可以在自定义的dao中实现
 * @param <T>
 */
public interface RedisBaseDao<T>{
    
    /**
     * 根据keyId查询实体
     * @param cls
     * @param keyId 以什么参数作为keyid
     * @return
     */
    public T getEntityByKey(final Class<T> cls, final String keyId);
    
    /**
     * 新增实体
     * @param entity
     * @param keyId
     */
    public void addEntity(final T entity, final String keyId);
    
    /**
     * 新增实体(设置过期时间)
     * @param entity
     * @param keyId
     * @param seconds 多少秒后过期
     */
    public void addEntity(final T entity, final String keyId, long seconds);

    /**
     * 根据keyId删除实体
     * @param cls
     * @param keyId
     */
    public void deleteEntityByKey(final Class<T> cls, final String keyId);

}

RedisBaseDaoImpl实现类

package net.ibloger.wechat.redis.dao;

import java.io.Serializable;

import javax.annotation.Resource;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.util.Assert;

import com.google.gson.Gson;

/**
 * 实现Redis通用基类
 * 
 * @author http://www.ibloger.net
 * @param <T>
 */
public class RedisBaseDaoImpl<T> implements RedisBaseDao<T> {
    
    @Resource
    protected RedisTemplate<Serializable, Serializable> redisTemplate;

    /**
     * 获取 RedisSerializer
     */
    protected RedisSerializer<String> getStringSerializer() {
        return redisTemplate.getStringSerializer();
    }

    @Override
    public T getEntityByKey(final Class<T> cls, final String keyId) {
        return redisTemplate.execute(new RedisCallback<T>() {
            @Override
            public T doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key = getStringSerializer().serialize(cls.getName() + "_" + keyId);
                if (connection.exists(key)) {
                    byte[] value = connection.get(key);
                    String json = getStringSerializer().deserialize(value);
                    return new Gson().fromJson(json, cls);
                }
                return null;
            }
        });
    }

    @Override
    public void addEntity(final T entity, final String keyId) {
        Assert.notNull(entity);
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key =  getStringSerializer().serialize(entity.getClass().getName() + "_" + keyId);
                byte[] name = getStringSerializer().serialize(new Gson().toJson(entity));
                connection.set(key,name);
                return null;
            }
        });
    }
    
    @Override
    public void addEntity(final T entity, final String keyId, final long seconds) {
        Assert.notNull(entity);
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key =  getStringSerializer().serialize(entity.getClass().getName() + "_" + keyId);
                byte[] name = getStringSerializer().serialize(new Gson().toJson(entity));
                connection.setEx(key, seconds, name);
                System.out.println("connection.getClientName(): "+connection.getClientName());
                return null;
            }
            
            
        });
    }

    @Override
    public void deleteEntityByKey(Class<T> cls, String keyId) {
        redisTemplate.delete(cls.getName() + "_" + keyId);
    }

}

RedisKeyConfig,保存key配置

package net.ibloger.wechat.redis;

/**
 * Redis服务器存储Key的配置
 * @author X-rapido
 *
 */
public class RedisKeyConfig {
	
	/**
	 * 微信消息AccessToken
	 */
	public static String WX_Message_Access_Token = "WX_M_A_T";
}

AccessToken基类

package net.ibloger.wechat.core.token;

import java.io.Serializable;

/**
 * 微信通用接口凭证
 * 
 * @author X-rapido
 * @description
 */
public class AccessToken implements Serializable{
    private static final long serialVersionUID = -8906695447214573189L;
    
    private String token;   // 获取到的凭证
    private int expiresIn;  // 凭证有效时间,单位:秒
    private long invalidDate; // 失效时间
    
    public AccessToken(){}
    public AccessToken(String token, int expiresIn){
        this.token = token;
        this.expiresIn = expiresIn;
    }
    
    // 省略getter和setter
    
    @Override
    public String toString() {
        return "AccessToken [token=" + token + ", expiresIn=" + expiresIn + ", invalidData=" + invalidDate + "]";
    }
    
}

AccessTokenDao

package net.ibloger.wechat.core.token;

public interface AccessTokenDao {
    
    /**
     * 添加token到redis缓存中(设置过期)
     * @param accessToken
     * @param seconds (秒)
     * @return
     */
    void addAccessToken(AccessToken accessToken, long seconds);

    /**
     * 从redis中删除token
     */
    void deleteAccessToken();

    /**
     * 从redis缓存中获取token
     * @return AccessToken
     */
    AccessToken getAccessToken();
    
}

AccessTokenDaoImpl

package net.ibloger.wechat.core.token;

import org.springframework.stereotype.Repository;

import net.ibloger.wechat.redis.RedisKeyConfig;
import net.ibloger.wechat.redis.dao.RedisBaseDaoImpl;

@Repository(value = "AccessTokenDao")
public class AccessTokenDaoImpl extends RedisBaseDaoImpl<AccessToken> implements AccessTokenDao{

    @Override
    public void deleteAccessToken() {
        deleteEntityByKey(AccessToken.class, RedisKeyConfig.WX_Message_Access_Token);
    }

    @Override
    public AccessToken getAccessToken() {
        return getEntityByKey(AccessToken.class, RedisKeyConfig.WX_Message_Access_Token);
    }

    @Override
    public void addAccessToken(AccessToken accessToken, long seconds) {
        addEntity(accessToken, RedisKeyConfig.WX_Message_Access_Token, seconds);
    }
    
}

AccessTokenService

package net.ibloger.wechat.core.token;

import java.util.Date;

import javax.annotation.Resource;

import net.ibloger.wechat.core.WeChatConfig;
import net.ibloger.wechat.utils.DateUtils;
import net.ibloger.wechat.utils.HttpProxyUtil;
import net.ibloger.wechat.utils.SpringUtil;
import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

/**
 * AccessToken服务类
 * @author X-rapido 
 * @description 获取AccessToken对象,将对象保存在redis缓存中处理
 */
@Service(value="accessTokenService")
public class AccessTokenService {
    private static Logger logger = Logger.getLogger(AccessTokenService.class);
    
    @Resource(name = "AccessTokenDao")
    private AccessTokenDao accessTokenDao;
    
    /**
     * 获取AccessToken对象
     * @return
     */
    public AccessToken getAccessToken(){
        return getAccessToken(false);
    }
    
    /**
     * 重新获取AccessToken对象
     * @return
     */
    public AccessToken getReqAccessToken(){
        return getAccessToken(true);
    }
    
    /**
     * 获取AccessToken对象
     * @param reReq 是否重新请求AccessToken,在过期时候调用,默认为false
     * @return
     */
    public AccessToken getAccessToken(boolean reReq){
        if(reReq){
            logger.info("重新获取AccessToken");
            return reqAccessToken();
        }else {
            AccessToken accessToken = accessTokenDao.getAccessToken();
            // 判断时间是否过期
            if (accessToken != null && (accessToken.getInvalidDate() > System.currentTimeMillis())) {
                logger.info("缓存获取AccessToken,失效时间为:"+DateUtils.getStrByDate(new Date(accessToken.getInvalidDate()),"yyyy-MM-dd HH:mm:ss"));
                return accessToken;
            }else {
                logger.info("缓存过期,重新获取AccessToken");
                return reqAccessToken();
            }
        }
    }
    
    /**
     * 获取服务器的AccessToken对象并存储在redis缓存中, 默认生效2小时,GET方式,限200(次/天)
     * @return
     */
    private AccessToken reqAccessToken() {
        try {
            String api_url = WeChatConfig.access_token_url.replace("{appid}", WeChatConfig.app_id).replace("{secret}", WeChatConfig.app_secret);
            String result = HttpProxyUtil.sendGet(api_url);
            if (StringUtils.isNotEmpty(result)) {
                JSONObject jsonObject = JSONObject.fromObject(result);
                AccessToken accessToken = new AccessToken();
                accessToken.setToken(jsonObject.get("access_token").toString());
                accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
                accessToken.setInvalidDate(System.currentTimeMillis()+accessToken.getExpiresIn()*1000); // 取毫秒设置过期时间
                accessTokenDao.addAccessToken(accessToken,accessToken.getExpiresIn());  // 添加设置到redis缓存中
                return accessToken;
            }
        } catch (Exception e) {
            logger.error("获取服务器的accessToken失败 "+ e.getMessage());
        }
        return null;
    }

    public static void main(String[] args) {
        // 获取accessToken服务类
        AccessTokenService service = (AccessTokenService) SpringUtil.getBean("accessTokenService");
        String token = service.getAccessToken().getToken();
        if (null != token) {
            System.out.println("token: " + token);
        }
    }
}


未经允许请勿转载:程序喵 » 微信公众号开发教程第19篇——使用Spring+Redis方式保存access_token

点  赞 (3) 打  赏
分享到: