Redis的使用
pom
需要加入cache包及redis包
<!--緩存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
yml
配置redis的基本參數
##redis配置澄阳,默認密碼為空
redis:
host: localhost
# Redis服務器連接端口
port: 6379
jedis:
pool:
#連接池最大連接數(使用負值表示沒有限制)
max-active: 100
# 連接池中的最小空閑連接
max-idle: 10
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
max-wait: 100000
# 連接超時時間(毫秒)
timeout: 5000
#默認是索引為0的數據庫
database: 0
redis配置類
需要配置能夠使用@Cacheable、@CacheEvict注解來進行緩存,同時也需要使用redisTemplate進行一些操作玲躯,所以要配置CacheManager和RedisTemplate模聋,Springboot使用的序列化方式為jdkSerializable,存儲的是二進制字節(jié)碼料睛,不易讀并且很長丐箩,于是使用Jackson2JsonRedisSerializer做為值的序列化方法,鍵的序列化方法還是使用String恤煞。
@Configuration
@EnableCaching
@Slf4j
public class RedisConfig {
private static final Duration timeToLive = Duration.ofDays(1);
/**
* 采用RedisCacheManager作為緩存管理器
* @param connectionFactory
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 1.設置redis緩存配置
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(timeToLive) //設置過期時間
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer())) // 設置鍵的序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer())) // 設置值得序列化方式
.disableCachingNullValues(); //不緩存空值
// 2.建立redis緩存管理
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(redisCacheConfiguration)
.transactionAware()
.build();
log.info("自定義RedisCacheManager加載完成");
return redisCacheManager;
}
/**
* redisTemplate 序列化使用的jdkSerializeable, 存儲二進制字節(jié)碼, 所以自定義序列化類
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 1.建立redis模板類
RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 2.設置value的序列化規(guī)則和 key的序列化規(guī)則
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setHashValueSerializer(keySerializer());
redisTemplate.afterPropertiesSet();
log.info("自定義RedisTemplate加載完成");
return redisTemplate;
}
/**
* 鍵的序列化方法
* @return
*/
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
/**
* 值的序列化方法
* @return
*/
private RedisSerializer<Object> valueSerializer() {
// 1.創(chuàng)建 序列化類
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
// 2.設置可見度
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 3.啟動默認的類型
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// 4.序列化類屎勘,對象映射設置
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
}
實體類到map的互換
為了可以直接將實體存到redis的hash類型,寫一個實體與map轉換的工具類居扒,需要引入beanutils的依賴:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
public class EntityUtils {
public static Map<String, String> objectToHash(Object obj) {
try {
Map<String, String> map = new HashMap();
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor property : propertyDescriptors) {
if (!property.getName().equals("class")) {
if (property.getReadMethod().invoke(obj) != null) {
// 時間類型會錯亂所以吧時間手動轉換成long;
if (property.getReadMethod().invoke(obj) != null) {
if ("java.util.Date".equals(property.getPropertyType().getTypeName())) {
Date invoke = (Date) property.getReadMethod().invoke(obj);
long time = invoke.getTime();
map.put(property.getName(), String.valueOf(time));
} else {
map.put(property.getName(), "" + property.getReadMethod().invoke(obj));
}
}
}
}
}
return map;
} catch (InvocationTargetException | IllegalAccessException | IntrospectionException e) {
throw new RuntimeException(e);
}
}
public static <T> T hashToObject(Map<?, ?> map, Class t) {
// 轉換注冊器
ConvertUtils.register(new LongConverter(null), Long.class);
ConvertUtils.register(new ByteConverter(null), Byte.class);
ConvertUtils.register(new IntegerConverter(null), Integer.class);
ConvertUtils.register(new DoubleConverter(null), Double.class);
ConvertUtils.register(new ShortConverter(null), Short.class);
ConvertUtils.register(new FloatConverter(null), Float.class);
ConvertUtils.register(new Converter() {
public Object convert(Class type, Object value) {
if (value == null) {
return null;
}
return new Date(Long.valueOf((String) value));
}
}, Date.class);
try {
Object o = t.newInstance();
BeanUtils.populate(o, (Map) map);
return (T) o;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
}
這樣就可以在項目中使用redis概漱,并且可以方便的把實體類直接存入redis的hash類型中了。
微信小程序
pom
<!--微信小程序SDK-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>3.3.0</version>
</dependency>
yml
wechat:
appId: 小程序appid
appSecret: 小程序secret
小程序賬戶配置
@Component
@Data
@ConfigurationProperties(prefix = "wechat")
public class WeChatAccountCogfig {
/**
* 公眾平臺id
*/
private String appId;
/**
* 公眾平臺secret
*/
private String appSecret;
}
小程序配置類
@Configuration
public class WeChatMaConfig {
@Autowired
WeChatAccountCogfig weChatAccountCogfig;
@Bean
public WxMaService wxMaService(){
WxMaService wxMaService = new WxMaServiceImpl();
wxMaService.setWxMaConfig(wxMaConfigStorage());
return wxMaService;
}
@Bean
public WxMaInMemoryConfig wxMaConfigStorage(){
WxMaInMemoryConfig wxMaInMemoryConfig = new WxMaInMemoryConfig();
wxMaInMemoryConfig.setAppid(weChatAccountCogfig.getAppId());
wxMaInMemoryConfig.setSecret(weChatAccountCogfig.getAppSecret());
return wxMaInMemoryConfig;
}
}
小程序配置也完成了喜喂,我們看一下微信小程序登錄流程瓤摧。
登錄流程
說明:
- 調用 wx.login() 獲取 臨時登錄憑證code ,并回傳到開發(fā)者服務器玉吁。
- 調用 auth.code2Session 接口照弥,換取 用戶唯一標識 OpenID 和 會話密鑰 session_key。
之后開發(fā)者服務器可以根據用戶標識來生成自定義登錄態(tài)进副,用于后續(xù)業(yè)務邏輯中前后端交互時識別用戶身份这揣。
注意:
- 會話密鑰
session_key
是對用戶數據進行 加密簽名 的密鑰。為了應用自身的數據安全影斑,開發(fā)者服務器不應該把會話密鑰下發(fā)到小程序给赞,也不應該對外提供這個密鑰。 - 臨時登錄憑證 code 只能使用一次
了解了基本的登錄流程矫户,我們就可以開始寫小程序的登錄模塊了片迅。
小程序登錄Controller
主要分為兩個部分,登錄模塊和獲取用戶信息模塊皆辽。對登錄模塊柑蛇,選擇redis進行3rd_sessionId
的存儲,3rd_sessionId
的生成使用sessionKey+openId+currentTimeMillis()
進行MD5
加密后的結果驱闷,當重復登錄時需要使之前的3rd_sessionId
失效耻台,所以需要使用鍵sessionId
保存session
信息,用鍵openId
保存sessionId
信息遗嗽,當發(fā)現有當前用戶的3rd_sessionId
還沒失效粘我,又進行的登錄,則刪除舊的3rd_sessionId
,將3rd_sessionId
有效期設置為30天征字。
獲取sessionKey
及解密獲得用戶信息都是使用了微信小程序SDK都弹,很好用的SDK,還有配套的demo匙姜,推薦給大家畅厢。
@RestController
@Slf4j
@RequestMapping("/wechat")
public class WeChatController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private WxMaService wxMaService;
@Autowired
private UserServiceImpl userService;
@GetMapping("/login")
public ResultVO WeChatLogin(@RequestParam String code){
String sessionId;
try{
// 0.如果code為空,返回錯誤信息
if (code==null||code.isEmpty()){
return ResultVOUtil.error(ResultEnum.CODE_ERROR.getCode(), ResultEnum.CODE_ERROR.getMessage());
}
// 1.向微信服務器獲取openid和sessionKey
WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
if (session==null){
return ResultVOUtil.error(ResultEnum.SESSION_ERROR.getCode(), ResultEnum.SESSION_ERROR.getMessage());
}
String sessionKey = session.getSessionKey();
String openId = session.getOpenid();
// 3.根據openid查詢用戶是否存在
User user = userService.findUser(session.getOpenid());
// 4.若用戶不存在則創(chuàng)建用戶
if (user == null){
userService.createUser(session.getOpenid());
}
// 5.查看redis中是否有登錄信息
if (redisTemplate.hasKey("openId::" + openId)){
redisTemplate.delete(redisTemplate.opsForValue().get("openId::" + openId));
}
// 6.生成加密的sessionId;
sessionId = KeyUtil.getSessionId(sessionKey+openId+System.currentTimeMillis());
// 7.存入redis中
redisTemplate.opsForValue().set("sessionId::" + sessionId, session,30, TimeUnit.DAYS);
redisTemplate.opsForValue().set("openId::" + openId, "sessionId::" + sessionId, 30, TimeUnit.DAYS);
}catch (WxErrorException e){
log.error(e.getMessage(), e);
return ResultVOUtil.error(e.getError().getErrorCode(), e.getError().getErrorMsg());
}
// 8.返回第三方sessionId交由客戶端保存
HashMap<String, String> map = new HashMap();
map.put("sessionId", sessionId);
return ResultVOUtil.success(map);
}
@PostMapping("/info")
public ResultVO<String> WeChatInfo(@RequestParam String sessionId,
@RequestBody UserInfoForm userInfoForm){
String rawData = userInfoForm.getRawData();
String signature = userInfoForm.getSignature();
String encryptedData = userInfoForm.getEncryptedData();
String iv = userInfoForm.getIv();
// 1.查看是否有sessionId信息
if (!redisTemplate.hasKey("sessionId::" + sessionId)){
return ResultVOUtil.error(ResultEnum.SESSION_ID_NULL.getCode(), ResultEnum.SESSION_ID_NULL.getMessage());
}
// 2.從sessionId中取出sessionKey
WxMaJscode2SessionResult session = (WxMaJscode2SessionResult) redisTemplate.opsForValue().get("sessionId::" + sessionId);
String sessionKey = session.getSessionKey();
// 3.校驗用戶信息
if (!wxMaService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
return ResultVOUtil.error(ResultEnum.USER_INFO_ERROR.getCode(), ResultEnum.USER_INFO_ERROR.getMessage());
}
// 4.解密用戶信息
WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionKey, encryptedData, iv);
// 5.更新用戶信息
User user = userService.updateUser(userInfo);
return ResultVOUtil.success(user);
}
}
至此氮昧,小程序登錄的模塊已經完成框杜,下一章會介紹主要的業(yè)務模塊,包括帖子袖肥、評論及上傳圖片到七牛云對象存儲咪辱。
上一篇:springboot+jpa+redis+quzartz+elasticsearch實現微信論壇小程序(二)
下一篇:springboot+jpa+redis+quzartz+elasticsearch實現微信論壇小程序(四)