提交 0650ed47 编写于 作者: 街头小贩's avatar 街头小贩

thrones增加OnlineMemberRedisStorage

上级 2d937203
......@@ -147,8 +147,8 @@
<version>${spring-framework.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<groupId>org.springframework</groupId>
<artifactId>spring-jcl</artifactId>
</exclusion>
</exclusions>
</dependency>
......@@ -215,6 +215,12 @@
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
<version>2.3.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-jcl</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
......
package com.apobates.forum.member.storage.redis;
import com.apobates.forum.member.storage.MetaConfig;
import com.apobates.forum.member.storage.OnlineMemberStorage;
import com.apobates.forum.member.storage.cookie.CookieMetaConfig;
import com.apobates.forum.member.storage.core.MemberSessionBean;
import com.apobates.forum.member.storage.core.MemberSessionBeanConverter;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.CookieUtils;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
/**
* 使用Redis存储会员在线信息,Cookie中存储的是票根
* 支持redis 3.0.5
*
* @author xiaofanku
* @since 20200908
*/
public class OnlineMemberRedisStorage implements OnlineMemberStorage{
@Autowired
private CookieMetaConfig metaConfig;
@Autowired
private RedisTemplate<String, Object> template;
private final static Logger logger = LoggerFactory.getLogger(OnlineMemberRedisStorage.class);
@Override
public void store(MemberSessionBean memberSessionBean, HttpServletRequest request, HttpServletResponse response) {
if(!isContinue()){
return;
}
String passStub = rndStub();
Optional<String> afterPart = hash(passStub, memberSessionBean.getIpAddr()); //前缀:hashValue
Map<String, String> val = MemberSessionBeanConverter.toMap(memberSessionBean);
if (afterPart.isPresent() && null != val && !val.isEmpty()) {
String key = metaConfig.getName() + ":" + afterPart.get();
template.opsForHash().putAll(key, val);
if (template.expire(key, 1, TimeUnit.DAYS)) {
serializeCookie(passStub, request, response, metaConfig.getName(), metaConfig.getPath(), metaConfig.getDomain(), metaConfig.isHttps());
}
}
}
@Override
public Optional<MemberSessionBean> query(long memberId) {
return Optional.empty();
}
@Override
public void delete(HttpServletRequest request, HttpServletResponse response) {
if(!isContinue()){
return;
}
Cookie cookie = CookieUtils.queryCookie(request, metaConfig.getName()).orElse(null);
if (null == cookie) {
return;
}
String passStub = cookie.getValue();
Optional<String> afterPart = hash(passStub, Commons.getRequestIp(request)); //前缀:hashValue
if (afterPart.isPresent()) {
if (template.delete(metaConfig.getName() + ":" + afterPart.get())) {
expireCookie(request, response, metaConfig.getName(), metaConfig.getPath(), metaConfig.getDomain());
}
}
}
@Override
public Optional<MemberSessionBean> getInstance(HttpServletRequest request, String sentinel) {
if(!isContinue()){
return Optional.empty();
}
Cookie cookie = CookieUtils.queryCookie(request, metaConfig.getName()).orElse(null);
if (null == cookie) {
return Optional.empty();
}
String passStub = cookie.getValue();
Optional<String> afterPart = hash(passStub, Commons.getRequestIp(request)); //前缀:hashValue
if (afterPart.isPresent()) {
String redisKey = metaConfig.getName() + ":" + afterPart.get();
HashOperations<String, String, String> operation = template.opsForHash();
Map<String, String> val = operation.entries(redisKey);
if (null != val && !val.isEmpty()) {
return MemberSessionBeanConverter.toInstance(val, Commons.getRequestIp(request), sentinel);
}
}
return Optional.empty();
}
@Override
public boolean isSupportRevival() {
return true;
}
@Override
public MetaConfig getMetaConfig() {
return metaConfig;
}
private boolean isContinue(){
return null != template;
}
/**
* 序列化会员信息到Cookie中 A positive value indicates that the cookie will expire
* after that many seconds have passed.Note that the value is the maximum
age when the cookie will expire, not the cookie's current age. A negative
value means that the cookie is not stored persistently and will be
deleted when the Web browser exits. A zero value causes the cookie to be
deleted.
*
* @param cookieValue cookie中保存的值
* @param request
* @param response Http响应对象
* @param cookieSymbol cookie的Key
* @param cookiePath cookie的路径
* @param cookieDomain cookie的域名
* @param isHttps work on HTTPS/true,false work on http
*/
protected void serializeCookie(
String cookieValue,
HttpServletRequest request,
HttpServletResponse response,
String cookieSymbol,
String cookiePath,
String cookieDomain,
boolean isHttps) {
try {
CookieUtils.serializeCookie(cookieValue, 60 * 60 * 24 * 1, request, response, cookieSymbol, cookiePath, cookieDomain, isHttps);
} catch (IllegalStateException e) {
if (logger.isDebugEnabled()) {
logger.debug("[OM][CS]write cookie fail, reason: " + e.getMessage(), e);
}
}
}
/**
* 清空Cookie中的会员信息
*
* @param request Http请求对象
* @param response Http响应对象
* @param cookieSymbol cookie的Key
* @param cookiePath cookie的路径
* @param cookieDomain cookie的域名
*/
protected void expireCookie(
HttpServletRequest request,
HttpServletResponse response,
String cookieSymbol,
String cookiePath,
String cookieDomain) {
CookieUtils.expireCookie(request, response, cookieSymbol, cookiePath, cookieDomain);
}
/**
*
* @param stub 长度12位
* @param ipAddr ipv4
* @return
*/
private Optional<String> hash(String stub, String ipAddr){ //redis key的长度
if(!Commons.isNotBlank(stub) || !Commons.isNotBlank(ipAddr)){
return Optional.empty();
}
return Optional.of(stub);
}
private String rndStub(){
return UUID.randomUUID().toString().substring(24);
}
}
\ No newline at end of file
......@@ -36,6 +36,8 @@ import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.time.Duration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
/**
* Spring framework配置类
......@@ -159,6 +161,10 @@ public class ThronesAppConfig {
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(cf);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
@Bean
......
......@@ -5,12 +5,15 @@ import com.apobates.forum.attention.ImageStoreDomain;
import com.apobates.forum.core.ImageIOMeta;
import com.apobates.forum.member.storage.OnlineMemberStorage;
import com.apobates.forum.member.storage.cookie.CookieMetaConfig;
import com.apobates.forum.member.storage.redis.OnlineMemberRedisStorage;
import com.apobates.forum.thrones.controller.helper.AuthenticationInterceptor;
import com.apobates.forum.thrones.controller.helper.MemberInviteCodeInterceptorAdapter;
import com.apobates.forum.thrones.controller.helper.OnlineDescriptorAspect;
import com.apobates.forum.thrones.controller.helper.RedisMemberStorageCondition;
import com.apobates.forum.thrones.controller.helper.RegisteChannelInterceptor;
import com.apobates.forum.thrones.controller.helper.StrategyInterceptorAdapter;
import com.apobates.forum.thrones.rss.TopicRssView;
import com.apobates.forum.utils.Commons;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
......@@ -23,7 +26,10 @@ import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
......@@ -205,11 +211,19 @@ public class ThronesFrontConfig implements WebMvcConfigurer {
return cnf;
}
@Bean(name = "onlineMemberStorage")
public OnlineMemberStorage getMemberStorage(CookieMetaConfig cookieConfig) {
//cookie
return new com.apobates.forum.member.storage.cookie.HttpCookieProvider(cookieConfig);
public OnlineMemberStorage getMemberStorage(CookieMetaConfig cookieConfig, @Nullable OnlineMemberRedisStorage redisStorageProvider) {
if(null == redisStorageProvider){
//cookie
return new com.apobates.forum.member.storage.cookie.HttpCookieProvider(cookieConfig);
}
return redisStorageProvider;
}
@Bean
@Conditional(RedisMemberStorageCondition.class)
public OnlineMemberRedisStorage getMemberRedisStorage(){
//redis
return new com.apobates.forum.member.storage.redis.OnlineMemberRedisStorage();
}
@Bean(name="imageStoreDomain")
public ImageStoreDomain getImageStore(){
return new com.apobates.forum.attention.ImageStoreDomain(){
......
package com.apobates.forum.thrones.controller.helper;
import com.apobates.forum.utils.Commons;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
......@@ -17,13 +16,6 @@ public class RedisMemberStorageCondition implements Condition{
@Override
public boolean matches(ConditionContext cc, AnnotatedTypeMetadata atm) {
Environment env = cc.getEnvironment();
if(null == env){
return false;
}
return Commons.isNotBlank(env.getProperty("redis.host")) &&
Commons.isNotBlank(env.getProperty("redis.port")) &&
Commons.isNotBlank(env.getProperty("redis.timeout")) &&
Commons.isNotBlank(env.getProperty("redis.database")) &&
Commons.isNotBlank(env.getProperty("redis.password"));
return null != env && "true".equalsIgnoreCase(env.getProperty("site.member.redis"));
}
}
\ No newline at end of file
......@@ -27,6 +27,7 @@ site.defat.avtar=avatar
site.member.freeze=10
site.member.invite=false
site.member.register=true
site.member.redis=true
site.meta.description=In this tutorial we show a Spring MVC RSS ATOM Content Negotiation example. Using Content Negotiation we can serve different versions of a document (or resource) at the same URI.
# \u83b7\u53d6\u5931\u8d25\u65f6\u663e\u793a\u7684\u56fe\u7247
site.upload.fail=static/image/photo_album.jpg
......@@ -55,12 +56,6 @@ jpa.vendor=MYSQL
jpa.log=/home/admin/logs/jforum-sql.log
jpa.plat=org.eclipse.persistence.platform.database.MySQLPlatform
jpa.batch.size=1000
# redis member storage
# redis.host=127.0.0.1
# redis.port=6379
# redis.timeout=60
# redis.password=jedisroot
# redis.database=0
# redis cache provider
cache.redis.host=127.0.0.1
cache.redis.port=6379
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册