mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 23:23:55 +08:00
Merge pull request #1060 from GOODDAYDAY/feature/config-token
feat: 增加server通用secret过滤器
This commit is contained in:
+3
-18
@@ -1,6 +1,6 @@
|
||||
package xiaozhi.modules.config.controller;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -9,13 +9,10 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.common.validator.ValidatorUtils;
|
||||
import xiaozhi.modules.config.dto.AgentModelsDTO;
|
||||
import xiaozhi.modules.config.service.ConfigService;
|
||||
import xiaozhi.modules.sys.dto.ConfigSecretDTO;
|
||||
import xiaozhi.modules.sys.service.SysParamsService;
|
||||
|
||||
/**
|
||||
@@ -33,29 +30,17 @@ public class ConfigController {
|
||||
|
||||
@PostMapping("server-base")
|
||||
@Operation(summary = "获取配置")
|
||||
public Result<Object> getConfig(@RequestBody ConfigSecretDTO dto) {
|
||||
// 效验数据
|
||||
ValidatorUtils.validateEntity(dto);
|
||||
checkSecret(dto.getSecret());
|
||||
public Result<Object> getConfig() {
|
||||
Object config = configService.getConfig(true);
|
||||
return new Result<Object>().ok(config);
|
||||
}
|
||||
|
||||
@PostMapping("agent-models")
|
||||
@Operation(summary = "获取智能体模型")
|
||||
public Result<Object> getAgentModels(@RequestBody AgentModelsDTO dto) {
|
||||
public Result<Object> getAgentModels(@Valid @RequestBody AgentModelsDTO dto) {
|
||||
// 效验数据
|
||||
ValidatorUtils.validateEntity(dto);
|
||||
checkSecret(dto.getSecret());
|
||||
Object models = configService.getAgentModels(dto.getMacAddress(), dto.getSelectedModule());
|
||||
return new Result<Object>().ok(models);
|
||||
}
|
||||
|
||||
private void checkSecret(String secret) {
|
||||
String secretParam = sysParamsService.getValue(Constant.SERVER_SECRET, true);
|
||||
// 验证密钥
|
||||
if (StringUtils.isBlank(secret) || !secret.equals(secretParam)) {
|
||||
throw new RenException("密钥错误");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package xiaozhi.modules.security.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.session.mgt.SessionManager;
|
||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||
@@ -14,10 +11,14 @@ import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import xiaozhi.modules.security.oauth2.Oauth2Filter;
|
||||
import xiaozhi.modules.security.oauth2.Oauth2Realm;
|
||||
import xiaozhi.modules.security.secret.ServerSecretFilter;
|
||||
import xiaozhi.modules.sys.service.SysParamsService;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Shiro的配置文件
|
||||
@@ -46,7 +47,7 @@ public class ShiroConfig {
|
||||
}
|
||||
|
||||
@Bean("shiroFilter")
|
||||
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
|
||||
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, SysParamsService sysParamsService) {
|
||||
ShiroFilterConfiguration config = new ShiroFilterConfiguration();
|
||||
config.setFilterOncePerRequest(true);
|
||||
|
||||
@@ -54,9 +55,11 @@ public class ShiroConfig {
|
||||
shiroFilter.setSecurityManager(securityManager);
|
||||
shiroFilter.setShiroFilterConfiguration(config);
|
||||
|
||||
// oauth过滤
|
||||
Map<String, Filter> filters = new HashMap<>();
|
||||
// oauth过滤
|
||||
filters.put("oauth2", new Oauth2Filter());
|
||||
// 服务密钥过滤
|
||||
filters.put("server", new ServerSecretFilter(sysParamsService));
|
||||
shiroFilter.setFilters(filters);
|
||||
|
||||
// 添加Shiro的内置过滤器
|
||||
@@ -79,8 +82,8 @@ public class ShiroConfig {
|
||||
filterMap.put("/user/login", "anon");
|
||||
filterMap.put("/user/pub-config", "anon");
|
||||
filterMap.put("/user/register", "anon");
|
||||
filterMap.put("/config/server-base", "anon");
|
||||
filterMap.put("/config/agent-models", "anon");
|
||||
// 将config路径使用server服务过滤器
|
||||
filterMap.put("/config/**", "server");
|
||||
filterMap.put("/**", "oauth2");
|
||||
shiroFilter.setFilterChainDefinitionMap(filterMap);
|
||||
|
||||
@@ -98,4 +101,4 @@ public class ShiroConfig {
|
||||
advisor.setSecurityManager(securityManager);
|
||||
return advisor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package xiaozhi.modules.security.secret;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.utils.HttpContextUtils;
|
||||
import xiaozhi.common.utils.JsonUtils;
|
||||
import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.modules.sys.service.SysParamsService;
|
||||
|
||||
/**
|
||||
* Config API 过滤器
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ServerSecretFilter extends AuthenticatingFilter {
|
||||
private final SysParamsService sysParamsService;
|
||||
|
||||
@Override
|
||||
protected ServerSecretToken createToken(ServletRequest request, ServletResponse response) {
|
||||
// 获取请求token
|
||||
String token = getRequestToken((HttpServletRequest) request);
|
||||
|
||||
if (StringUtils.isBlank(token)) {
|
||||
log.warn("createToken:token is empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ServerSecretToken(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||
// 对OPTIONS请求放行
|
||||
if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
|
||||
// 获取token并校验
|
||||
String token = getRequestToken((HttpServletRequest) servletRequest);
|
||||
if (StringUtils.isBlank(token)) {
|
||||
// token为空,返回401
|
||||
this.sendUnauthorizedResponse((HttpServletResponse) servletResponse, "Authorization token不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证token是否匹配
|
||||
String serverSecret = getServerSecret();
|
||||
if (StringUtils.isBlank(serverSecret) || !serverSecret.equals(token)) {
|
||||
// token无效,返回401
|
||||
this.sendUnauthorizedResponse((HttpServletResponse) servletResponse, "无效的Authorization token");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送未授权响应
|
||||
*/
|
||||
private void sendUnauthorizedResponse(HttpServletResponse response, String message) {
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
response.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
|
||||
|
||||
try {
|
||||
String json = JsonUtils.toJsonString(new Result<Void>().error(ErrorCode.UNAUTHORIZED, message));
|
||||
response.getWriter().print(json);
|
||||
} catch (IOException e) {
|
||||
log.error("响应输出失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求的token
|
||||
*/
|
||||
private String getRequestToken(HttpServletRequest httpRequest) {
|
||||
String token = null;
|
||||
// 从header中获取token
|
||||
String authorization = httpRequest.getHeader("Authorization");
|
||||
if (StringUtils.isNotBlank(authorization) && authorization.startsWith("Bearer ")) {
|
||||
token = authorization.replace("Bearer ", "");
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private String getServerSecret() {
|
||||
return sysParamsService.getValue(Constant.SERVER_SECRET, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package xiaozhi.modules.security.secret;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
|
||||
/**
|
||||
* Config API Token
|
||||
*/
|
||||
public class ServerSecretToken implements AuthenticationToken {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final String token;
|
||||
|
||||
public ServerSecretToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package xiaozhi.modules.sys.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "配置密钥DTO")
|
||||
public class ConfigSecretDTO {
|
||||
@Schema(description = "密钥")
|
||||
@NotBlank(message = "密钥不能为空")
|
||||
private String secret;
|
||||
}
|
||||
Reference in New Issue
Block a user