From d5a84ac64ec67f532a8e67b211384f367230d15e Mon Sep 17 00:00:00 2001 From: vertoryao Date: Fri, 20 Jun 2025 17:08:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(dify):=20=E6=B7=BB=E5=8A=A0=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=B7=A5=E4=BD=9C=E6=B5=81=E5=85=B3=E8=81=94=E7=9A=84?= =?UTF-8?q?=E9=83=A8=E9=97=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 16 ++++++++++++++++ .../security/CustomAccessDeniedHandler.java | 10 +++++----- .../CustomSessionInformationExpiredStrategy.java | 4 +++- .../framework/security/SpringSecurityConfig.java | 11 +++++++---- .../dify/controller/V1ChatController.java | 5 ++++- .../dify/controller/V1ServerController.java | 5 +++++ .../controller/OperationController.java | 6 +++--- 7 files changed, 43 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 707d6a6..77bbcc9 100644 --- a/pom.xml +++ b/pom.xml @@ -132,6 +132,22 @@ provided + + + + + + + + + + + + + + + + org.apache.commons commons-lang3 diff --git a/src/main/java/com/zsc/edu/dify/framework/security/CustomAccessDeniedHandler.java b/src/main/java/com/zsc/edu/dify/framework/security/CustomAccessDeniedHandler.java index deafa51..e0ee566 100644 --- a/src/main/java/com/zsc/edu/dify/framework/security/CustomAccessDeniedHandler.java +++ b/src/main/java/com/zsc/edu/dify/framework/security/CustomAccessDeniedHandler.java @@ -6,6 +6,7 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; +import org.springframework.beans.factory.BeanFactory; import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authorization.AuthorizationDeniedException; @@ -38,11 +39,10 @@ public class CustomAccessDeniedHandler implements AccessDeniedHandler { response.setStatus(HttpStatus.UNAUTHORIZED.value()); result = new ExceptionResult("凭证已过期,请重新登录", HttpStatus.UNAUTHORIZED.value(), LocalDateTime.now()); - } else if (ex instanceof AuthorizationDeniedException) { - // 会话已存在,禁止重复登录,返回401 - response.setStatus(HttpStatus.UNAUTHORIZED.value()); - result = new ExceptionResult("当前账号已在其他设备登录,请先退出再尝试登录", HttpStatus.UNAUTHORIZED.value(), - LocalDateTime.now()); +// } else if (ex instanceof AuthorizationDeniedException) { +// response.setStatus(HttpStatus.FORBIDDEN.value()); +// result = new ExceptionResult("权限不足", HttpStatus.FORBIDDEN.value(), +// LocalDateTime.now()); } else { // 403 response.setStatus(HttpStatus.FORBIDDEN.value()); diff --git a/src/main/java/com/zsc/edu/dify/framework/security/CustomSessionInformationExpiredStrategy.java b/src/main/java/com/zsc/edu/dify/framework/security/CustomSessionInformationExpiredStrategy.java index 4c59a9e..c909f5b 100644 --- a/src/main/java/com/zsc/edu/dify/framework/security/CustomSessionInformationExpiredStrategy.java +++ b/src/main/java/com/zsc/edu/dify/framework/security/CustomSessionInformationExpiredStrategy.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component; import java.io.IOException; import java.time.LocalDateTime; +import java.util.Date; import java.util.Map; /** @@ -26,8 +27,9 @@ public class CustomSessionInformationExpiredStrategy implements SessionInformati response.getWriter().print(objectMapper.writeValueAsString(Map.of( "msg", "会话已过期(有可能是您同时登录了太多的客户端)", "code", HttpStatus.UNAUTHORIZED.value(), - "timestamp", LocalDateTime.now() + "timestamp", new Date() ))); + response.flushBuffer(); } } \ No newline at end of file diff --git a/src/main/java/com/zsc/edu/dify/framework/security/SpringSecurityConfig.java b/src/main/java/com/zsc/edu/dify/framework/security/SpringSecurityConfig.java index 7c09f2d..cf354b0 100644 --- a/src/main/java/com/zsc/edu/dify/framework/security/SpringSecurityConfig.java +++ b/src/main/java/com/zsc/edu/dify/framework/security/SpringSecurityConfig.java @@ -10,6 +10,8 @@ import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.web.server.SecurityWebFiltersOrder; +import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.core.userdetails.UserDetailsService; @@ -20,11 +22,13 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy; -import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.session.HttpSessionEventPublisher; import javax.sql.DataSource; +import static org.springframework.security.config.Customizer.withDefaults; + /** * @author harry_yao */ @@ -39,7 +43,6 @@ public class SpringSecurityConfig { private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; private final CustomAccessDeniedHandler customAccessDeniedHandler; private final CustomSessionInformationExpiredStrategy customSessionInformationExpiredStrategy; -// private final SessionRegistry sessionRegistry; @Resource private final DataSource dataSource; @@ -63,7 +66,7 @@ public class SpringSecurityConfig { public ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy() { ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry()); concurrentSessionControlAuthenticationStrategy.setMaximumSessions(1); - concurrentSessionControlAuthenticationStrategy.setExceptionIfMaximumExceeded(true); + concurrentSessionControlAuthenticationStrategy.setExceptionIfMaximumExceeded(false); return concurrentSessionControlAuthenticationStrategy; } @@ -126,7 +129,7 @@ public class SpringSecurityConfig { .ignoringRequestMatchers("v1/**","/api/internal/**", "/api/rest/user/logout","/api/rest/user/register")) .sessionManagement(session -> session .maximumSessions(1) - .maxSessionsPreventsLogin(true) + .maxSessionsPreventsLogin(false) .sessionRegistry(sessionRegistry()) .expiredSessionStrategy(customSessionInformationExpiredStrategy) ).build(); diff --git a/src/main/java/com/zsc/edu/dify/modules/dify/controller/V1ChatController.java b/src/main/java/com/zsc/edu/dify/modules/dify/controller/V1ChatController.java index 3095ef6..c2f5388 100644 --- a/src/main/java/com/zsc/edu/dify/modules/dify/controller/V1ChatController.java +++ b/src/main/java/com/zsc/edu/dify/modules/dify/controller/V1ChatController.java @@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import java.util.List; +import java.util.Map; /** * @author yanghq @@ -45,6 +46,7 @@ public class V1ChatController { * apikey 建议在数据库进行存储,前端调用时传智能体 id,从数据库查询 */ @PostMapping("/completions/{appId}") + @PreAuthorize("hasAuthority('difyChat:query')") @OperationLogAnnotation(content = "'dify对话'", operationType = "发送") public ChatMessageSendResponse sendChatMessage( @RequestBody ChatMessageSendRequest sendRequest, @@ -125,11 +127,12 @@ public class V1ChatController { */ @DeleteMapping("/conversation") @OperationLogAnnotation(content = "'dify对话'", operationType = "删除") - public void deleteConversation(@RequestParam String conversationId, @RequestParam String appId) { + public Map deleteConversation(@RequestParam String conversationId, @RequestParam String appId) { String apiKey = appEntityService.getApikey(appId); String userId = SecurityUtil.getUserInfo().id.toString(); try{ difyChat.deleteConversation(conversationId, apiKey, userId); + return Map.of("message", "删除成功"); }catch (RuntimeException e){ throw new ApiException("删除会话失败"+e.getMessage()); } diff --git a/src/main/java/com/zsc/edu/dify/modules/dify/controller/V1ServerController.java b/src/main/java/com/zsc/edu/dify/modules/dify/controller/V1ServerController.java index bf144c4..e7191e5 100644 --- a/src/main/java/com/zsc/edu/dify/modules/dify/controller/V1ServerController.java +++ b/src/main/java/com/zsc/edu/dify/modules/dify/controller/V1ServerController.java @@ -139,4 +139,9 @@ public class V1ServerController { ResponseEntity.ok("关联成功") : ResponseEntity.status(HttpStatus.BAD_REQUEST).body("关联失败"); } + + @GetMapping("/link/{workflowId}") + public List linked(@PathVariable String workflowId) { + return workflowDeptService.lambdaQuery().eq(WorkflowDept::getWorkflowId, workflowId).list(); + } } diff --git a/src/main/java/com/zsc/edu/dify/modules/operationLog/controller/OperationController.java b/src/main/java/com/zsc/edu/dify/modules/operationLog/controller/OperationController.java index 707bada..5fa8700 100644 --- a/src/main/java/com/zsc/edu/dify/modules/operationLog/controller/OperationController.java +++ b/src/main/java/com/zsc/edu/dify/modules/operationLog/controller/OperationController.java @@ -24,7 +24,7 @@ public class OperationController { * 获取操作日志详情 */ @GetMapping("/{id}") - @PreAuthorize("hasAuthority('operationLog:query')") + @PreAuthorize("hasAuthority('system:operationLog:query')") public OperationLog crate(@PathVariable("id") Long id) { return repo.selectById(id); } @@ -33,7 +33,7 @@ public class OperationController { * 获取操作日志分页 */ @GetMapping("") - @PreAuthorize("hasAuthority('operationLog:query')") + @PreAuthorize("hasAuthority('system:operationLog:query')") public Page query(Page page, OperationLogQuery query) { return repo.selectPage(page, query.wrapper()); } @@ -42,7 +42,7 @@ public class OperationController { * 批量删除操作日志 */ @DeleteMapping("/batch") - @PreAuthorize("hasAuthority('operationLog:delete')") + @PreAuthorize("hasAuthority('system:operationLog:delete')") public int deleteBatch(@RequestBody List ids) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.in(OperationLog::getId, ids);