diff --git a/pom.xml b/pom.xml index 4520883..1547933 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,5 @@ - 4.0.0 @@ -144,6 +144,8 @@ spring-boot-maven-plugin 2.7.1 + true + true org.projectlombok diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/RedisConfig.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/RedisConfig.java index 1e9e476..5920f86 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/RedisConfig.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/RedisConfig.java @@ -1,10 +1,12 @@ package cn.edu.hfut.rmdjzz.projectmanagement.config; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; 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.RedisSerializer; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import javax.annotation.Resource; @@ -16,16 +18,20 @@ import javax.annotation.Resource; public class RedisConfig { @Resource - RedisConnectionFactory factory; + private RedisConnectionFactory factory; + @Autowired + private ObjectMapper objectMapper; @Bean - public RedisTemplate redisTemplate() { - RedisTemplate template = new RedisTemplate<>(); + public RedisTemplate redisTemplate() { + RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(factory); - template.setKeySerializer(RedisSerializer.json()); - template.setValueSerializer(RedisSerializer.json()); - template.setHashKeySerializer(RedisSerializer.json()); - template.setHashValueSerializer(RedisSerializer.json()); + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + template.setKeySerializer(jackson2JsonRedisSerializer); + template.setValueSerializer(jackson2JsonRedisSerializer); + template.setHashKeySerializer(jackson2JsonRedisSerializer); + template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/SerializeConfig.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/SerializeConfig.java new file mode 100644 index 0000000..eaa19b1 --- /dev/null +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/SerializeConfig.java @@ -0,0 +1,80 @@ +package cn.edu.hfut.rmdjzz.projectmanagement.config; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.IOException; +import java.time.*; + +/** + * Jackson序列化配置,主要配置了{@link LocalDateTime} {@link LocalDate} {@link LocalTime}转为秒级时间戳的序列化 + * + * @author 佘语殊 + * @since 2022/6/28 23:59 + */ +@Configuration +public class SerializeConfig { + + @Bean + public ObjectMapper objectMapper() { + + ObjectMapper om = new ObjectMapper(); + JavaTimeModule javaTimeModule = new JavaTimeModule(); + + //LocalDataTime + javaTimeModule.addSerializer(LocalDateTime.class, new JsonSerializer<>() { + @Override + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value != null) { + gen.writeNumber(value.toEpochSecond(ZoneOffset.UTC)); + } + } + }); + javaTimeModule.addDeserializer(LocalDateTime.class, new JsonDeserializer<>() { + @Override + public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return Instant.ofEpochSecond(p.getValueAsLong(), 0).atOffset(ZoneOffset.UTC).toLocalDateTime(); + } + }); + + //LocalDate + javaTimeModule.addSerializer(LocalDate.class, new JsonSerializer<>() { + @Override + public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value != null) { + gen.writeNumber(value.toEpochSecond(LocalTime.MIN, ZoneOffset.UTC)); + } + } + }); + javaTimeModule.addDeserializer(LocalDate.class, new JsonDeserializer<>() { + @Override + public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return Instant.ofEpochSecond(p.getValueAsLong(), 0).atOffset(ZoneOffset.UTC).toLocalDate(); + } + }); + + //LocalTime,处理为1970-1-1 + javaTimeModule.addSerializer(LocalTime.class, new JsonSerializer<>() { + @Override + public void serialize(LocalTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value != null) { + gen.writeNumber(value.toEpochSecond(LocalDate.of(1970, 1, 1), ZoneOffset.UTC)); + } + } + }); + javaTimeModule.addDeserializer(LocalTime.class, new JsonDeserializer<>() { + @Override + public LocalTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return Instant.ofEpochSecond(p.getValueAsLong(), 0).atOffset(ZoneOffset.UTC).toLocalTime(); + } + }); + + om.registerModule(javaTimeModule); + return om; + } + +} diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/WebConfig.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/WebConfig.java index 373bf83..8eca476 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/WebConfig.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/config/WebConfig.java @@ -2,15 +2,11 @@ package cn.edu.hfut.rmdjzz.projectmanagement.config; import cn.edu.hfut.rmdjzz.projectmanagement.interceptor.CorsInterceptor; import cn.edu.hfut.rmdjzz.projectmanagement.interceptor.TokenInterceptor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import javax.annotation.Resource; -import java.security.Permission; - /** * @author 张韬 * created at 2022/6/28 19:44 @@ -18,16 +14,24 @@ import java.security.Permission; @Configuration public class WebConfig implements WebMvcConfigurer { @Bean - public CorsInterceptor getCorsInterceptor(){return new CorsInterceptor();} + public CorsInterceptor getCorsInterceptor() { + return new CorsInterceptor(); + } + @Bean - public TokenInterceptor getTokenInterceptor(){return new TokenInterceptor();} + public TokenInterceptor getTokenInterceptor() { + return new TokenInterceptor(); + } + @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(getCorsInterceptor()).addPathPatterns("/**"); registry.addInterceptor(getTokenInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/staff/login") - .excludePathPatterns("/hello"); + .excludePathPatterns("/hello") + .excludePathPatterns("/error") + .excludePathPatterns("/staff/timetest"); } /* @Override diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/StaffController.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/StaffController.java index 8b349a4..2c33999 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/StaffController.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/StaffController.java @@ -6,12 +6,10 @@ import cn.edu.hfut.rmdjzz.projectmanagement.service.impl.StaffServiceImpl; import cn.edu.hfut.rmdjzz.projectmanagement.utils.response.ResponseMap; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; +import java.time.LocalDateTime; /** * @author 张韬 @@ -31,12 +29,16 @@ public class StaffController { @SneakyThrows @PostMapping("/logout") - public ResponseMap logout(HttpServletRequest request) { - String token= request.getHeader("Token"); - if(staffServiceImpl.logout(token)) + public ResponseMap logout(@RequestHeader("Token") String token) { + if (staffServiceImpl.logout(token)) return ResponseMap.ofSuccess("操作成功"); - else{ + else { throw new TokenException("操作失败"); } } + + @GetMapping("/timetest") + public LocalDateTime timeTest(){ + return LocalDateTime.now(); + } } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/BadRequestException.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/BadRequestException.java index 43984ee..11521ed 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/BadRequestException.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/BadRequestException.java @@ -4,6 +4,8 @@ package cn.edu.hfut.rmdjzz.projectmanagement.exception; * @author 张韬 * created at 2022/6/28 21:24 */ -public class BadRequestException extends Exception{ - public BadRequestException(String message){super(message);} +public class BadRequestException extends Exception { + public BadRequestException(String message) { + super(message); + } } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/TokenException.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/TokenException.java index c725f24..e87f6d0 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/TokenException.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/TokenException.java @@ -4,6 +4,9 @@ package cn.edu.hfut.rmdjzz.projectmanagement.exception; * @author 张韬 * created at 2022/6/28 23:34 */ -public class TokenException extends Exception{ - public TokenException(String message){super(message);} +//FIXME: 是否加入RequestUrl与RequestMethod作为错误信息log到日志 +public class TokenException extends Exception { + public TokenException(String message) { + super(message); + } } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/interceptor/TokenInterceptor.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/interceptor/TokenInterceptor.java index 266b78d..a0c582e 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/interceptor/TokenInterceptor.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/interceptor/TokenInterceptor.java @@ -3,19 +3,11 @@ package cn.edu.hfut.rmdjzz.projectmanagement.interceptor; import cn.edu.hfut.rmdjzz.projectmanagement.exception.TokenException; import cn.edu.hfut.rmdjzz.projectmanagement.utils.TokenUtils; -import cn.edu.hfut.rmdjzz.projectmanagement.utils.response.ResponseMap; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; -import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; -import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -26,35 +18,39 @@ import java.util.concurrent.TimeUnit; * @author 张韬 * created at 2022/6/28 18:16 */ -//FIXME: objectMapper去掉 @Component public class TokenInterceptor implements HandlerInterceptor { - @Resource - private RedisTemplate redisTemplate; + + @Autowired + private RedisTemplate redisTemplate; + @Override - public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws TokenException, IOException { - System.out.println(httpServletRequest.getRequestURL()+" "+httpServletRequest.getMethod()); + public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws TokenException { + System.out.println(httpServletRequest.getRequestURL() + " " + httpServletRequest.getMethod()); String token = httpServletRequest.getHeader("Token"); if (null == token || "".equals(token.trim())) { throw new TokenException("缺少Token"); } - if(!TokenUtils.checkToken(token)){ + if (!TokenUtils.checkToken(token)) { throw new TokenException("无效的Token"); } - if(TokenUtils.checkTimeOut(token)){ + if (TokenUtils.checkTimeOut(token)) { throw new TokenException("Token已过期"); } System.out.println(Objects.requireNonNull(TokenUtils.getStaffId(token))); System.out.println(token); - if(!token.equals(redisTemplate.opsForValue().get(Objects.requireNonNull(TokenUtils.getStaffId(token))))){ + if (!token.equals(redisTemplate.opsForValue().get(Objects.requireNonNull(TokenUtils.getStaffId(token))))) { throw new TokenException("请重新登录"); } - String newToken=TokenUtils.autoRequire(token); - if(!newToken.equals(token)){ - redisTemplate.opsForValue().set(Objects.requireNonNull(TokenUtils.getStaffId(token)), - token,Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS); + String newToken = TokenUtils.autoRequire(token); + if (!newToken.equals(token)) { + redisTemplate.opsForValue().set( + Objects.requireNonNull(TokenUtils.getStaffId(token)), + token, + Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS + ); } - httpServletResponse.setHeader("Token",newToken); + httpServletResponse.setHeader("Token", newToken); return true; } } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/IStaffService.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/IStaffService.java index cad1051..d64e4e8 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/IStaffService.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/IStaffService.java @@ -12,5 +12,6 @@ import com.baomidou.mybatisplus.extension.service.IService; */ public interface IStaffService extends IService { ResponseMap login(String username, String password) throws BadRequestException, TokenException; + Boolean logout(String token) throws TokenException; } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/StaffServiceImpl.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/StaffServiceImpl.java index 57e6d4d..39fd671 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/StaffServiceImpl.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/StaffServiceImpl.java @@ -9,16 +9,11 @@ import cn.edu.hfut.rmdjzz.projectmanagement.utils.TokenUtils; import cn.edu.hfut.rmdjzz.projectmanagement.utils.response.ResponseMap; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.fasterxml.jackson.core.JsonProcessingException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; -import javax.annotation.PostConstruct; -import javax.annotation.Resource; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -28,9 +23,10 @@ import java.util.concurrent.TimeUnit; */ @Service public class StaffServiceImpl extends ServiceImpl implements IStaffService { - private static final Long tokenDuration=5*60*60L; - @Resource - private RedisTemplate redisTemplate; + private static final Long tokenDuration = 5 * 60 * 60L; + + @Autowired + private RedisTemplate redisTemplate; @Override public ResponseMap login(String staffUsername, String password) throws BadRequestException, TokenException { @@ -47,25 +43,28 @@ public class StaffServiceImpl extends ServiceImpl implements password = DigestUtils.md5DigestAsHex((password + staff.getStaffSalt()).getBytes()); if (!staff.getStaffPassword().equals(password)) throw new BadRequestException("密码错误"); - String token=TokenUtils.getToken(staff.getStaffUsername(), staff.getStaffId(),tokenDuration); - redisTemplate.opsForValue().set(Objects.requireNonNull(TokenUtils.getStaffId(token)), - token,Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS); + String token = TokenUtils.getToken(staff.getStaffUsername(), staff.getStaffId(), tokenDuration); + redisTemplate.opsForValue().set( + Objects.requireNonNull(TokenUtils.getStaffId(token)), + token, + Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS + ); return ResponseMap.ofSuccess("ok") - .put("Token",token) + .put("Token", token) .put("staffUsername", staffUsername) .put("staffFullname", staff.getStaffFullname()) .put("staffId", staff.getStaffId()); } + @Override public Boolean logout(String token) throws TokenException { - Integer staffId=TokenUtils.getStaffId(token); - if(staffId==null) + Integer staffId = TokenUtils.getStaffId(token); + if (staffId == null) return false; - String remoteToken= redisTemplate.opsForValue().get(staffId); - if(!token.equals(remoteToken)) + String remoteToken = (String) redisTemplate.opsForValue().get(staffId); + if (!token.equals(remoteToken)) return false; redisTemplate.delete(staffId); return true; - } } diff --git a/src/test/java/cn/edu/hfut/rmdjzz/projectmanagement/RedisTests.java b/src/test/java/cn/edu/hfut/rmdjzz/projectmanagement/RedisTests.java index 7c2435d..71cf4b7 100644 --- a/src/test/java/cn/edu/hfut/rmdjzz/projectmanagement/RedisTests.java +++ b/src/test/java/cn/edu/hfut/rmdjzz/projectmanagement/RedisTests.java @@ -1,10 +1,11 @@ package cn.edu.hfut.rmdjzz.projectmanagement; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; -import javax.annotation.Resource; +import java.time.LocalDateTime; /** * @author 佘语殊 @@ -12,11 +13,16 @@ import javax.annotation.Resource; */ @SpringBootTest public class RedisTests { - @Resource + @Autowired private RedisTemplate redisTemplate; @Test - void test(){ - redisTemplate.opsForList().rightPush(123456,89); + void test() { + redisTemplate.opsForList().rightPush(123456, 89); + } + + @Test + void testValue() { + redisTemplate.opsForValue().set("time", LocalDateTime.now()); } } diff --git a/src/test/java/cn/edu/hfut/rmdjzz/projectmanagement/SerializeTests.java b/src/test/java/cn/edu/hfut/rmdjzz/projectmanagement/SerializeTests.java new file mode 100644 index 0000000..f037042 --- /dev/null +++ b/src/test/java/cn/edu/hfut/rmdjzz/projectmanagement/SerializeTests.java @@ -0,0 +1,27 @@ +package cn.edu.hfut.rmdjzz.projectmanagement; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * @author 佘语殊 + * @since 2022/6/29 17:25 + */ +@SpringBootTest +public class SerializeTests { + + @Autowired + private ObjectMapper objectMapper; + + @SneakyThrows + @Test + public void serializeTime() { + System.out.println(objectMapper.readValue("1656523481", LocalDate.class)); + } +}