diff --git a/pom.xml b/pom.xml index 6710ef7..340d5d7 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ zkh-common zkh-web zkh-data + zkh-log @@ -71,6 +72,11 @@ zkh-data ${project.version} + + vip.jcfd + zkh-log + ${project.version} + org.springdoc springdoc-openapi-common diff --git a/zkh-data/pom.xml b/zkh-data/pom.xml index 7bb4ad8..e15d51b 100644 --- a/zkh-data/pom.xml +++ b/zkh-data/pom.xml @@ -13,12 +13,6 @@ ZKH Data Data layer components for ZKH framework - - 21 - 21 - UTF-8 - - jakarta.persistence diff --git a/zkh-log/pom.xml b/zkh-log/pom.xml new file mode 100644 index 0000000..960722d --- /dev/null +++ b/zkh-log/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + vip.jcfd + zkh-framework + 1.4 + + + zkh-log + ZKH log + Logging utilities for ZKH framework + + + + org.springframework.boot + spring-boot-starter-aop + + + + diff --git a/zkh-log/src/main/java/vip/jcfd/log/annotation/Log.java b/zkh-log/src/main/java/vip/jcfd/log/annotation/Log.java new file mode 100644 index 0000000..1c2d8c9 --- /dev/null +++ b/zkh-log/src/main/java/vip/jcfd/log/annotation/Log.java @@ -0,0 +1,10 @@ +package vip.jcfd.log.annotation; + +import java.lang.annotation.*; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Log { + String value(); +} diff --git a/zkh-log/src/main/java/vip/jcfd/log/annotation/config/LogConfig.java b/zkh-log/src/main/java/vip/jcfd/log/annotation/config/LogConfig.java new file mode 100644 index 0000000..498044d --- /dev/null +++ b/zkh-log/src/main/java/vip/jcfd/log/annotation/config/LogConfig.java @@ -0,0 +1,7 @@ +package vip.jcfd.log.annotation.config; + +import org.springframework.context.annotation.Configuration; + +@Configuration("_logConfiguration") +public class LogConfig { +} diff --git a/zkh-web/pom.xml b/zkh-web/pom.xml index 48b5718..5fed6dd 100644 --- a/zkh-web/pom.xml +++ b/zkh-web/pom.xml @@ -19,6 +19,10 @@ vip.jcfd zkh-common + + vip.jcfd + zkh-log + org.springframework.boot spring-boot-starter-web diff --git a/zkh-web/src/main/java/vip/jcfd/web/config/GlobalExceptionHandler.java b/zkh-web/src/main/java/vip/jcfd/web/config/GlobalExceptionHandler.java index 0bea6c3..c6a9b2a 100644 --- a/zkh-web/src/main/java/vip/jcfd/web/config/GlobalExceptionHandler.java +++ b/zkh-web/src/main/java/vip/jcfd/web/config/GlobalExceptionHandler.java @@ -13,35 +13,35 @@ import vip.jcfd.common.core.R; import java.util.List; -@RestControllerAdvice +@RestControllerAdvice("_globalExceptionHandler") public class GlobalExceptionHandler { - private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); - @ExceptionHandler(value = Exception.class) - public R handleException(Exception e) { - log.error("服务异常", e); - return R.serverError("服务器繁忙,请稍候重试"); - } + @ExceptionHandler(value = Exception.class) + public R handleException(Exception e) { + log.error("服务异常", e); + return R.serverError("服务器繁忙,请稍候重试"); + } - @ExceptionHandler(value = BizException.class) - public R handleBizException(BizException e) { - log.error("业务异常", e); - return R.error(e.getMessage()); - } + @ExceptionHandler(value = BizException.class) + public R handleBizException(BizException e) { + log.error("业务异常", e); + return R.error(e.getMessage()); + } - @ExceptionHandler(value = NoResourceFoundException.class) - public R handleNotFoundException(NoResourceFoundException e) { - log.error("404异常", e); - return new R<>(404, "您访问的地址不存在", false, null); - } + @ExceptionHandler(value = NoResourceFoundException.class) + public R handleNotFoundException(NoResourceFoundException e) { + log.error("404异常", e); + return new R<>(404, "您访问的地址不存在", false, null); + } - @ExceptionHandler(value = BindException.class) - public R handleBindException(BindException e) { - log.error("接口入参校验失败", e); + @ExceptionHandler(value = BindException.class) + public R handleBindException(BindException e) { + log.error("接口入参校验失败", e); - List fieldErrors = e.getBindingResult().getFieldErrors(); - return R.error(String.join("。\n", fieldErrors.stream().map(FieldError::getDefaultMessage).toList())); - } + List fieldErrors = e.getBindingResult().getFieldErrors(); + return R.error(String.join("。\n", fieldErrors.stream().map(FieldError::getDefaultMessage).toList())); + } } diff --git a/zkh-web/src/main/java/vip/jcfd/web/config/RedisConfig.java b/zkh-web/src/main/java/vip/jcfd/web/config/RedisConfig.java index 98d4b97..1030922 100644 --- a/zkh-web/src/main/java/vip/jcfd/web/config/RedisConfig.java +++ b/zkh-web/src/main/java/vip/jcfd/web/config/RedisConfig.java @@ -10,26 +10,26 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; import vip.jcfd.web.config.props.SecurityProps; import vip.jcfd.web.redis.TokenRedisStorage; -@Configuration +@Configuration("_redisConfiguration") public class RedisConfig { - private final SecurityProps securityProps; + private final SecurityProps securityProps; - public RedisConfig(SecurityProps securityProps) { - this.securityProps = securityProps; - } + public RedisConfig(SecurityProps securityProps) { + this.securityProps = securityProps; + } - @Bean - public TokenRedisStorage tokenRedisTemplate(RedisConnectionFactory factory, StringRedisTemplate stringRedisTemplate, ObjectMapper objectMapper) { - TokenRedisStorage tokenRedisStorage = new TokenRedisStorage( - securityProps.getAccessTokenDuration(), - securityProps.getRefreshTokenDuration(), - stringRedisTemplate, - objectMapper - ); - tokenRedisStorage.setConnectionFactory(factory); - tokenRedisStorage.setValueSerializer(new JdkSerializationRedisSerializer()); - tokenRedisStorage.setKeySerializer(new StringRedisSerializer()); - return tokenRedisStorage; - } + @Bean + public TokenRedisStorage tokenRedisTemplate(RedisConnectionFactory factory, StringRedisTemplate stringRedisTemplate, ObjectMapper objectMapper) { + TokenRedisStorage tokenRedisStorage = new TokenRedisStorage( + securityProps.getAccessTokenDuration(), + securityProps.getRefreshTokenDuration(), + stringRedisTemplate, + objectMapper + ); + tokenRedisStorage.setConnectionFactory(factory); + tokenRedisStorage.setValueSerializer(new JdkSerializationRedisSerializer()); + tokenRedisStorage.setKeySerializer(new StringRedisSerializer()); + return tokenRedisStorage; + } } diff --git a/zkh-web/src/main/java/vip/jcfd/web/config/SpringDocConfig.java b/zkh-web/src/main/java/vip/jcfd/web/config/SpringDocConfig.java index 5c4026a..825c1d0 100644 --- a/zkh-web/src/main/java/vip/jcfd/web/config/SpringDocConfig.java +++ b/zkh-web/src/main/java/vip/jcfd/web/config/SpringDocConfig.java @@ -10,63 +10,63 @@ import org.springdoc.core.customizers.OpenApiCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -@Configuration("_springDocConfig") +@Configuration("_springDocConfiguration") public class SpringDocConfig { - @Bean - public OpenApiCustomizer openApiCustomizer() { - return (openAPI) -> { - openAPI.path("/login", new PathItem() - .post(new Operation() - .summary("登录接口") - .description("用于用户登录,返回token") - .addTagsItem("认证管理") - .requestBody(new RequestBody() - .description("帐号密码") - .required(true) - .content(new Content().addMediaType("application/json", new MediaType().schema(new Schema<>() - .addProperty("username", new StringSchema().example("admin")) - .addProperty("password", new StringSchema().example("123456")))))) - .responses(new ApiResponses() - .addApiResponse("成功", new ApiResponse() - .content(new Content().addMediaType("application/json", new MediaType().schema(new Schema<>() - .addProperty("data", new JsonSchema() - .addProperty("accessToken", new StringSchema().example("550e8400-e29b-41d4-a716-446655440000")) - .addProperty("refreshToken", new StringSchema().example("550e8400-e29b-41d4-a716-446655440001")) - .addProperty("tokenType", new StringSchema().example("Bearer")) - .addProperty("expiresIn", new NumberSchema().example(1800)) - .addProperty("username", new StringSchema().example("admin")) - ) - .addProperty("success", new BooleanSchema().example(true)) - .addProperty("code", new IntegerSchema().example(200)) - .addProperty("message", new StringSchema().example("登录成功")) - )))) - .addApiResponse("失败", new ApiResponse() - .content(new Content().addMediaType("application/json", new MediaType().schema(new Schema<>() - .addProperty("data", new StringSchema().example(null)) - .addProperty("success", new BooleanSchema().example(false)) - .addProperty("code", new IntegerSchema().example(401)) - .addProperty("message", new StringSchema().example("用户名或密码错误")) - )))) - ))); - openAPI.path("/logout", new PathItem() - .post(new Operation() - .summary("登出接口") - .description("用于用户登出") - .addTagsItem("认证管理") - .responses(new ApiResponses() - .addApiResponse("成功", new ApiResponse() - .content(new Content().addMediaType("application/json", new MediaType().schema(new Schema<>() - .addProperty("data", new StringSchema().example(null)) - .addProperty("success", new BooleanSchema().example(true)) - .addProperty("code", new IntegerSchema().example(200)) - .addProperty("message", new StringSchema().example("登出成功")) - ) - ) - ) - ) - ) - )); - }; - } + @Bean + public OpenApiCustomizer openApiCustomizer() { + return (openAPI) -> { + openAPI.path("/login", new PathItem() + .post(new Operation() + .summary("登录接口") + .description("用于用户登录,返回token") + .addTagsItem("认证管理") + .requestBody(new RequestBody() + .description("帐号密码") + .required(true) + .content(new Content().addMediaType("application/json", new MediaType().schema(new Schema<>() + .addProperty("username", new StringSchema().example("admin")) + .addProperty("password", new StringSchema().example("123456")))))) + .responses(new ApiResponses() + .addApiResponse("成功", new ApiResponse() + .content(new Content().addMediaType("application/json", new MediaType().schema(new Schema<>() + .addProperty("data", new JsonSchema() + .addProperty("accessToken", new StringSchema().example("550e8400-e29b-41d4-a716-446655440000")) + .addProperty("refreshToken", new StringSchema().example("550e8400-e29b-41d4-a716-446655440001")) + .addProperty("tokenType", new StringSchema().example("Bearer")) + .addProperty("expiresIn", new NumberSchema().example(1800)) + .addProperty("username", new StringSchema().example("admin")) + ) + .addProperty("success", new BooleanSchema().example(true)) + .addProperty("code", new IntegerSchema().example(200)) + .addProperty("message", new StringSchema().example("登录成功")) + )))) + .addApiResponse("失败", new ApiResponse() + .content(new Content().addMediaType("application/json", new MediaType().schema(new Schema<>() + .addProperty("data", new StringSchema().example(null)) + .addProperty("success", new BooleanSchema().example(false)) + .addProperty("code", new IntegerSchema().example(401)) + .addProperty("message", new StringSchema().example("用户名或密码错误")) + )))) + ))); + openAPI.path("/logout", new PathItem() + .post(new Operation() + .summary("登出接口") + .description("用于用户登出") + .addTagsItem("认证管理") + .responses(new ApiResponses() + .addApiResponse("成功", new ApiResponse() + .content(new Content().addMediaType("application/json", new MediaType().schema(new Schema<>() + .addProperty("data", new StringSchema().example(null)) + .addProperty("success", new BooleanSchema().example(true)) + .addProperty("code", new IntegerSchema().example(200)) + .addProperty("message", new StringSchema().example("登出成功")) + ) + ) + ) + ) + ) + )); + }; + } } diff --git a/zkh-web/src/main/java/vip/jcfd/web/config/WebSecurityConfig.java b/zkh-web/src/main/java/vip/jcfd/web/config/WebSecurityConfig.java index e798166..1da13c6 100644 --- a/zkh-web/src/main/java/vip/jcfd/web/config/WebSecurityConfig.java +++ b/zkh-web/src/main/java/vip/jcfd/web/config/WebSecurityConfig.java @@ -52,7 +52,7 @@ import java.io.IOException; import java.util.Optional; import java.util.UUID; -@Configuration +@Configuration("_webSecurityConfiguration") @EnableWebSecurity @ConfigurationPropertiesScan(basePackageClasses = {SecurityProps.class}) @EnableJpaAuditing @@ -60,191 +60,191 @@ import java.util.UUID; @EnableScheduling public class WebSecurityConfig { - private static final Logger log = LoggerFactory.getLogger(WebSecurityConfig.class); - private final SecurityProps securityProps; - private final ObjectMapper objectMapper; - private final TokenRedisStorage tokenRedisStorage; + private static final Logger log = LoggerFactory.getLogger(WebSecurityConfig.class); + private final SecurityProps securityProps; + private final ObjectMapper objectMapper; + private final TokenRedisStorage tokenRedisStorage; - public WebSecurityConfig(SecurityProps securityProps, - ObjectMapper objectMapper, - TokenRedisStorage tokenRedisStorage, - AuthenticationManagerBuilder builder, - UserDetailsService userDetailsService) { - this.securityProps = securityProps; - this.objectMapper = objectMapper; - this.tokenRedisStorage = tokenRedisStorage; - builder.authenticationProvider(new RefreshTokenAuthProvider(userDetailsService)); - DaoAuthenticationProvider authenticationProvider = new CustomDaoAuthenticationProvider(userDetailsService); - authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder()); - builder.authenticationProvider(authenticationProvider); - } + public WebSecurityConfig(SecurityProps securityProps, + ObjectMapper objectMapper, + TokenRedisStorage tokenRedisStorage, + AuthenticationManagerBuilder builder, + UserDetailsService userDetailsService) { + this.securityProps = securityProps; + this.objectMapper = objectMapper; + this.tokenRedisStorage = tokenRedisStorage; + builder.authenticationProvider(new RefreshTokenAuthProvider(userDetailsService)); + DaoAuthenticationProvider authenticationProvider = new CustomDaoAuthenticationProvider(userDetailsService); + authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder()); + builder.authenticationProvider(authenticationProvider); + } - @Scheduled(cron = "0 */30 * * * *") - @Async - public void scheduleClearExpiredTokens() { - tokenRedisStorage.clearExpiredTokens(); - } + @Scheduled(cron = "0 */30 * * * *") + @Async + public void scheduleClearExpiredTokens() { + tokenRedisStorage.clearExpiredTokens(); + } - @Bean - public AuditorAware auditorAware() { - return () -> Optional.ofNullable(SecurityContextHolder.getContext()) - .map(SecurityContext::getAuthentication) - .map(Authentication::getName) - .or(() -> Optional.of("system")); - } + @Bean + public AuditorAware auditorAware() { + return () -> Optional.ofNullable(SecurityContextHolder.getContext()) + .map(SecurityContext::getAuthentication) + .map(Authentication::getName) + .or(() -> Optional.of("system")); + } - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } - @Bean - @ConditionalOnMissingBean - public TokenFilter tokenFilter() { - return new TokenFilter(tokenRedisStorage); - } + @Bean + @ConditionalOnMissingBean + public TokenFilter tokenFilter() { + return new TokenFilter(tokenRedisStorage); + } - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { - return configuration.getAuthenticationManager(); - } + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { + return configuration.getAuthenticationManager(); + } - @Bean - public SecurityFilterChain security(HttpSecurity http, TokenFilter tokenFilter, AuthenticationManager authenticationManager) throws Exception { - http.authorizeHttpRequests(config -> { - config.requestMatchers(securityProps.getIgnoreUrls()).permitAll(); - config.anyRequest().authenticated(); - }); - CustomAuthenticationEntryPoint authenticationEntryPoint = new CustomAuthenticationEntryPoint(objectMapper, tokenRedisStorage); - http.formLogin(config -> { - config.loginProcessingUrl("/login"); - }); - http.csrf(AbstractHttpConfigurer::disable); - http.logout(config -> { - config.addLogoutHandler(new CustomLogoutSuccessHandler(objectMapper, tokenRedisStorage)); - }); - http.rememberMe(AbstractHttpConfigurer::disable); - http.sessionManagement(AbstractHttpConfigurer::disable); - http.exceptionHandling(config -> { - config.authenticationEntryPoint(authenticationEntryPoint); - config.accessDeniedHandler(new CustomAccessDeniedHandler(objectMapper)); - }); + @Bean + public SecurityFilterChain security(HttpSecurity http, TokenFilter tokenFilter, AuthenticationManager authenticationManager) throws Exception { + http.authorizeHttpRequests(config -> { + config.requestMatchers(securityProps.getIgnoreUrls()).permitAll(); + config.anyRequest().authenticated(); + }); + CustomAuthenticationEntryPoint authenticationEntryPoint = new CustomAuthenticationEntryPoint(objectMapper, tokenRedisStorage); + http.formLogin(config -> { + config.loginProcessingUrl("/login"); + }); + http.csrf(AbstractHttpConfigurer::disable); + http.logout(config -> { + config.addLogoutHandler(new CustomLogoutSuccessHandler(objectMapper, tokenRedisStorage)); + }); + http.rememberMe(AbstractHttpConfigurer::disable); + http.sessionManagement(AbstractHttpConfigurer::disable); + http.exceptionHandling(config -> { + config.authenticationEntryPoint(authenticationEntryPoint); + config.accessDeniedHandler(new CustomAccessDeniedHandler(objectMapper)); + }); - http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class); - JsonUsernamePasswordAuthenticationFilter filter = new JsonUsernamePasswordAuthenticationFilter(objectMapper, authenticationManager); - filter.setAuthenticationSuccessHandler(authenticationEntryPoint); - filter.setAuthenticationFailureHandler(authenticationEntryPoint); - http.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class); - return http.build(); - } + http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class); + JsonUsernamePasswordAuthenticationFilter filter = new JsonUsernamePasswordAuthenticationFilter(objectMapper, authenticationManager); + filter.setAuthenticationSuccessHandler(authenticationEntryPoint); + filter.setAuthenticationFailureHandler(authenticationEntryPoint); + http.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class); + return http.build(); + } - private record CustomAuthenticationEntryPoint( - ObjectMapper objectMapper, - TokenRedisStorage tokenRedisStorage) implements AuthenticationEntryPoint, AuthenticationFailureHandler, AuthenticationSuccessHandler { - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - log.warn("认证失败", authException); - R data = new R<>(HttpServletResponse.SC_UNAUTHORIZED, "未登录", false, null); - response.setContentType("application/json;charset=UTF-8"); - objectMapper.writeValue(response.getWriter(), data); - } + private record CustomAuthenticationEntryPoint( + ObjectMapper objectMapper, + TokenRedisStorage tokenRedisStorage) implements AuthenticationEntryPoint, AuthenticationFailureHandler, AuthenticationSuccessHandler { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + log.warn("认证失败", authException); + R data = new R<>(HttpServletResponse.SC_UNAUTHORIZED, "未登录", false, null); + response.setContentType("application/json;charset=UTF-8"); + objectMapper.writeValue(response.getWriter(), data); + } - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { - log.warn("登录失败", exception); - R data = new R<>(HttpServletResponse.SC_UNAUTHORIZED, "用户名或密码错误", false, null); - response.setContentType("application/json;charset=UTF-8"); - objectMapper.writeValue(response.getWriter(), data); - } + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + log.warn("登录失败", exception); + R data = new R<>(HttpServletResponse.SC_UNAUTHORIZED, "用户名或密码错误", false, null); + response.setContentType("application/json;charset=UTF-8"); + objectMapper.writeValue(response.getWriter(), data); + } - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - log.info("用户「{}」登录成功", authentication.getName()); + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + log.info("用户「{}」登录成功", authentication.getName()); - // 生成双重Token - String accessToken = UUID.randomUUID().toString(); - String refreshToken = UUID.randomUUID().toString(); + // 生成双重Token + String accessToken = UUID.randomUUID().toString(); + String refreshToken = UUID.randomUUID().toString(); - // 存储Access Token - tokenRedisStorage.putAccessToken(accessToken, authentication); + // 存储Access Token + tokenRedisStorage.putAccessToken(accessToken, authentication); - // 存储Refresh Token - String deviceId = extractDeviceId(request); - tokenRedisStorage.putRefreshToken(refreshToken, authentication.getName(), deviceId); + // 存储Refresh Token + String deviceId = extractDeviceId(request); + tokenRedisStorage.putRefreshToken(refreshToken, authentication.getName(), deviceId); - // 构造登录响应 - LoginResponse loginResponse = new LoginResponse( - accessToken, - refreshToken, - "Bearer", - 1800, // 30分钟,秒数 - authentication.getName() - ); + // 构造登录响应 + LoginResponse loginResponse = new LoginResponse( + accessToken, + refreshToken, + "Bearer", + 1800, // 30分钟,秒数 + authentication.getName() + ); - response.setContentType("application/json;charset=UTF-8"); - R data = new R<>(HttpServletResponse.SC_OK, "登录成功", true, loginResponse); - objectMapper.writeValue(response.getWriter(), data); - } + response.setContentType("application/json;charset=UTF-8"); + R data = new R<>(HttpServletResponse.SC_OK, "登录成功", true, loginResponse); + objectMapper.writeValue(response.getWriter(), data); + } - private String extractDeviceId(HttpServletRequest request) { - // 尝试从User-Agent提取设备信息 - String userAgent = request.getHeader("User-Agent"); - if (userAgent != null) { - // 简单的设备识别逻辑,生产环境可以使用更复杂的识别算法 - if (userAgent.contains("Mobile") || userAgent.contains("Android") || userAgent.contains("iPhone")) { - return "mobile-" + request.getRemoteAddr(); - } else if (userAgent.contains("Tablet") || userAgent.contains("iPad")) { - return "tablet-" + request.getRemoteAddr(); - } else { - return "desktop-" + request.getRemoteAddr(); - } - } - return "unknown-" + request.getRemoteAddr(); - } - } + private String extractDeviceId(HttpServletRequest request) { + // 尝试从User-Agent提取设备信息 + String userAgent = request.getHeader("User-Agent"); + if (userAgent != null) { + // 简单的设备识别逻辑,生产环境可以使用更复杂的识别算法 + if (userAgent.contains("Mobile") || userAgent.contains("Android") || userAgent.contains("iPhone")) { + return "mobile-" + request.getRemoteAddr(); + } else if (userAgent.contains("Tablet") || userAgent.contains("iPad")) { + return "tablet-" + request.getRemoteAddr(); + } else { + return "desktop-" + request.getRemoteAddr(); + } + } + return "unknown-" + request.getRemoteAddr(); + } + } - private record CustomAccessDeniedHandler(ObjectMapper objectMapper) implements AccessDeniedHandler { + private record CustomAccessDeniedHandler(ObjectMapper objectMapper) implements AccessDeniedHandler { - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - log.warn("访问被拒绝", accessDeniedException); - if (authentication.isAuthenticated()) { - log.warn("用户「{}」访问「{}」被拒绝,因为:{}", authentication.getPrincipal(), request.getRequestURI(), accessDeniedException.getMessage()); - } else { - log.warn("匿名用户访问「{}」被拒绝,因为:{}", request.getRequestURI(), accessDeniedException.getMessage()); - } - R data = new R<>(HttpServletResponse.SC_FORBIDDEN, "无权限", false, null); - response.setContentType("application/json;charset=UTF-8"); - objectMapper.writeValue(response.getWriter(), data); - } - } + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + log.warn("访问被拒绝", accessDeniedException); + if (authentication.isAuthenticated()) { + log.warn("用户「{}」访问「{}」被拒绝,因为:{}", authentication.getPrincipal(), request.getRequestURI(), accessDeniedException.getMessage()); + } else { + log.warn("匿名用户访问「{}」被拒绝,因为:{}", request.getRequestURI(), accessDeniedException.getMessage()); + } + R data = new R<>(HttpServletResponse.SC_FORBIDDEN, "无权限", false, null); + response.setContentType("application/json;charset=UTF-8"); + objectMapper.writeValue(response.getWriter(), data); + } + } - private record CustomLogoutSuccessHandler(ObjectMapper objectMapper, - TokenRedisStorage tokenRedisStorage) implements LogoutHandler { - @Override - public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { - String header = request.getHeader(HttpHeaders.AUTHORIZATION); + private record CustomLogoutSuccessHandler(ObjectMapper objectMapper, + TokenRedisStorage tokenRedisStorage) implements LogoutHandler { + @Override + public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { + String header = request.getHeader(HttpHeaders.AUTHORIZATION); - if (header != null && header.startsWith("Bearer ")) { - String token = header.substring(7); - authentication = tokenRedisStorage.get(token); - tokenRedisStorage.remove(token); - } - if (authentication != null) { - log.info("用户「{}」退出成功", authentication.getName()); - String all = request.getParameter("all"); - if ("true".equals(all)) { - tokenRedisStorage.removeByUserName(authentication.getName()); - } - } - R data = new R<>(HttpServletResponse.SC_OK, "退出成功", true, null); - response.setContentType("application/json;charset=UTF-8"); - try { - objectMapper.writeValue(response.getWriter(), data); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } + if (header != null && header.startsWith("Bearer ")) { + String token = header.substring(7); + authentication = tokenRedisStorage.get(token); + tokenRedisStorage.remove(token); + } + if (authentication != null) { + log.info("用户「{}」退出成功", authentication.getName()); + String all = request.getParameter("all"); + if ("true".equals(all)) { + tokenRedisStorage.removeByUserName(authentication.getName()); + } + } + R data = new R<>(HttpServletResponse.SC_OK, "退出成功", true, null); + response.setContentType("application/json;charset=UTF-8"); + try { + objectMapper.writeValue(response.getWriter(), data); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } } diff --git a/zkh-web/src/main/java/vip/jcfd/web/controller/AuthController.java b/zkh-web/src/main/java/vip/jcfd/web/controller/AuthController.java index 5f66bb5..0b1cca9 100644 --- a/zkh-web/src/main/java/vip/jcfd/web/controller/AuthController.java +++ b/zkh-web/src/main/java/vip/jcfd/web/controller/AuthController.java @@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.*; import vip.jcfd.common.core.R; import vip.jcfd.common.dto.TokenRefreshRequest; import vip.jcfd.common.dto.TokenRefreshResponse; +import vip.jcfd.log.annotation.Log; import vip.jcfd.web.auth.RefreshTokenAuthenticationToken; import vip.jcfd.web.redis.TokenRedisStorage; @@ -37,6 +38,7 @@ public class AuthController { @PostMapping("/refresh-token") @Operation(summary = "刷新Token", description = "使用Refresh Token获取新的Access Token和Refresh Token") + @Log("刷新了token") public R refreshToken( @Valid @RequestBody TokenRefreshRequest request) {