Hello-Java-Sec 项目 (代码审计)

news/2024/10/10 14:17:36

一、项目背景:

Hello-Java-Sec项目为 Github中 一个面向安全开发的 Java漏洞代码审计靶场。

靶场地址:https://github.com/j3ers3/Hello-Java-Sec 本地使用idea部署即可

image

二、代码审计:

通过阅读代码可知,代码采用 @RequestMapping 注解的方式来处理 HTTP不同方法的请求,故采用 全局搜索@RequestMapping注解来查看路由的方式,进行进一步的审计。

image

(一)、SQL注入:

1、JDBC注入:

(1)、语句拼接,过滤不当导致SQL注入:

image

未对用户传入的 id 值进行判断过滤,导致产生SQL注入

image

可采用黑名单的方式,对敏感字符进行过滤操作:

public static boolean checkSql(String content) {String[] black_list = {"'", ";", "--", "+", ",", "%", "=", ">", "*", "(", ")", "and", "or", "exec", "insert", "select", "delete", "update", "count", "drop", "chr", "mid", "master", "truncate", "char", "declare"};for (String s : black_list) {if (content.toLowerCase().contains(s)) {return true;}}return false;
}

(2)、预编译使用不当导致SQL注入:

image

虽然使用了 prepareStatement()方法进行预编译,但是并没有使用 ? 进行占位,导致代码本质上还是 sql语句的拼接,导致产生SQL注入。

可采用 ? 进行占位处理,修复代码如下:

public String safe1(String id) {String sql = "select * from users where id = ?";PreparedStatement st = conn.prepareStatement(sql);st.setString(1, id);ResultSet rs = st.executeQuery();
}

靶场还给出了另外两种可以防御SQL注入的代码,如下所示:
(1) ESAPI安全框架:

// ESAPI 是一个免费、开源的、网页应用程序安全控件库,它使程序员能够更容易写出更低风险的程序
// 官网:https://owasp.org/www-project-enterprise-security-api/public String safe3(String id) {Codec<Character> oracleCodec = new OracleCodec();Statement stmt = conn.createStatement();String sql = "select * from users where id = '" + ESAPI.encoder().encodeForSQL(oracleCodec, id) + "'";ResultSet rs = stmt.executeQuery(sql);
}

其中 encodeForSQL方法对输入的特殊字符进行了转义处理,例如单引号 ' 会被转义为 ''。

(2) 强制数据类型:

// 如果参数类型为boolean,byte,short,int,long,float,double等,sql语句无法拼接字符串,因此不存在注入public Map<String, Object> safe4(Integer id) {String sql = "select * from users where id = " + id;return jdbctemplate.queryForMap(sql);
}

若进行字符串拼接,会进行报错处理,提示我们需要输入一个 Integer类型,而不是String类型的值。
image

2、MyBatis框架中的SQL注入:

MyBatis框架:MyBatis 自动处理 JDBC 的繁琐工作,如建立连接、准备和执行 SQL 语句、处理结果集等。它简化了 JDBC 编程,提供了强大的动态 SQL 功能,并且支持多种数据库。

(1) 搜索框注入 ('%#{q}%' 和 '%${q}%' 使用不当):

image

用户输入的任意值被复制给参数q,并使用search()方法进行操作,跟进search()方法:
image

search()方法中采用 '%${q}%' 的方式进行SQL查询语句拼接,导致SQL注入的产生,因为由于使用 ${}语法,传入的参数值会直接插入到SQL查询语句中,未经过任何转义或处理,若其中包括敏感字符、恶意代码,就会导致SQL注入攻击。
image

解决此问题,可以采用 '%#{q}%' ,#{}语法会将传入的参数值作为预编译参数进行处理,对敏感字符进行转义,安全代码如下:

// 安全代码,采用concat
@Select("select * from users where user like concat('%',#{q},'%')")
List<User> search(String q);

也可以采取强制类型的方案进行修复,使其只能传入指定类型的值:

@Select("select * from users where id = ${id}")
List<User> queryById2(@Param("id") Integer id);

(2) order by注入:

order by注入产生的本质依然是 ${}语法的不当使用。

观察下图代码,调用了 userMapper接口中的 order by 方法,并传入参数 field 和 sort:
image

userMapper 是一个接口,通常是由 MyBatis 生成的,这个接口定义了与数据库进行交互的方法。userMapper接口中 order by 方法的实现通常在 MyBatis 的XML映射文件或注解中定义。

order by方法在 XML映射文件中的定义如下所示:
image

定义采用 ${} 语法,若 field = id; drop table users --,sort = asc,语句拼接就会组成恶意代码:

select * from users order by id;drop table users-- asc

同理,order by 方法在注解中的定义若使用了 ${}语法,同样也会产生SQL注入问题:
image

解决此问题,可以采用排序映射的方法:

<select id="orderBySafe" resultType="com.best.hello.entity.User">select * from users<choose><when test="field == 'id'">order by id desc</when><when test="field == 'user'">order by user desc</when><otherwise>order by id desc</otherwise></choose>
</select>

用户可输入的值只有 id 和 user两个,有效防止了SQL注入。

(二) 文件上传漏洞:

全局搜索 @RequestMapping("/UPLOAD") 寻找文件上传接口:
image

发现代码中未对上传文件后缀名做任何校验,导致用户可以上传任意文件,如 .php、.jsp等。

可以使用白名单对上传文件的后缀名进行规范:

// 对上传文件后缀名做白名单处理public String singleFileUploadSafe(@RequestParam("file") MultipartFile file,RedirectAttributes redirectAttributes) {try {byte[] bytes = file.getBytes();String fileName = file.getOriginalFilename();Path dir = Paths.get(UPLOADED_FOLDER);Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());// 获取文件后缀名String Suffix = fileName.substring(fileName.lastIndexOf("."));// 后缀名白名单String[] SuffixSafe = {".jpg", ".png", ".jpeg", ".gif", ".bmp", ".ico"};boolean flag = false;// 校验后缀名for (String s : SuffixSafe) {if (Suffix.toLowerCase().equals(s)) {flag = true;break;}}if (!flag) {return "只允许上传图片,[.jpg, .png, .jpeg, .gif, .bmp, .ico]";}

(三) 目录穿越 -> 任意文件读取/下载:

未对用户可控的 filename 的参数值进行过滤,导致使用 ../ 进行目录穿越,从而造成任意文件下载/读取:
image

目录穿越读取日志信息:
image

可以通过过滤 /字符 和 ..等非法字符的方式防止目录穿越:

// 过滤..和/ ,可以防止遍历操作public String safe(String filename) {if (!Security.checkTraversal(filename)) {  //对非法字符进行过滤String filePath = System.getProperty("user.dir") + "/logs/" + filename;return "安全路径:" + filePath;} else {return "检测到非法遍历";}
}public static boolean checkTraversal(String content) {return content.contains("..") || content.contains("/");
}

(四) XSS:

(1) 反射型XSS:

未对用户输入的内容进行字符转义与过滤处理,导致解析恶意JS代码,造成XSS攻击
image

防御手段有很多种:
1、使用 Spring自带的方法对特殊字符进行转义:

// 采用Spring自带的方法会对特殊字符全转义import org.springframework.web.util.HtmlUtils;@GetMapping("/safe1")
public static String safe1(String content) {return HtmlUtils.htmlEscape(content);
}

2、采用OWASP Java Encoder会对特殊字符全转义:

import org.owasp.encoder.Encode;
public static String safe5(String content){return Encode.forHtml(content);
}

3、使用ESAPI.encoder().encodeForHTML(content)方法对content中的特殊字符进行转义:

// ESAPI 是一个免费、开源的、网页应用程序安全控件库,它使程序员能够更容易写出更低风险的程序
// 官网:https://owasp.org/www-project-enterprise-security-api/import org.owasp.esapi.ESAPI;
public static String safe4(String content){return ESAPI.encoder().encodeForHTML(content);
}

对敏感字符进行转义后如下图所示:
image

4、富文本场景下使用黑白名单的方式,严格规范标签的使用,防止解析恶意JS代码:

// 场景:针对富文本的处理方式,需保留部分标签import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;public static String safe3(String content) {Safelist whitelist = (new Safelist()).addTags("p", "hr", "div", "img", "span", "textarea")  // 设置允许的标签.addAttributes("a", "href", "title")          // 设置标签允许的属性, 避免如nmouseover属性.addProtocols("img", "src", "http", "https")  // img的src属性只允许http和https开头.addProtocols("a", "href", "http", "https");return Jsoup.clean(content, whitelist);
}

(2) 存储型XSS:

论坛、社区等评论区等场景,未对用户输入的内容 content参数进行转义过滤,直接add添加,导致攻击者输入的恶意JS代码被页面解析,产生XSS漏洞。
image

修复本质上也是对输入的敏感字符进行转义处理:
1、使用org.springframework.web.util.HtmlUtils工具类中的 HtmlUtils.htmlEscape()方法对字符进行转义:

public String safeStore(HttpServletRequest request) {String content = request.getParameter("content");String safe_content = HtmlUtils.htmlEscape(content);xssMapper.add(safe_content);return "success";
}

2、前端输出转义:

$('#xssTableSafe tbody').append('<tr><td>' + item.id + '</td><td>' + $('<div/>').text(item.content).html() + '</td></tr>');

image

(五) SSRF漏洞:

(1) 未作任何限制的 SSRF漏洞:

未对用户可控的url参数做任何形式的限制和过滤,导致攻击者可以直接请求内网,以及利用file等伪协议读取敏感信息:
image

使用 file协议访问任意文件:
image

访问内网:
image

通常会采用白名单的方式进行修复,禁止攻击者使用除 HTTP/HTTPS协议之外的其他协议,并且禁止其访问内网:

public String URLConnection3(String url) {if (!Security.isHttp(url)) {return "不允许非http/https协议!!!";} else if (!Security.isWhite(url)) {return "非可信域名!";} else {return HttpClientUtil.URLConnection(url);}
}

但是这种白名单防御可以通过302跳转等方式进行绕过,所以更好的防御代码如下所示:

public String HTTPURLConnection(String url) {// 校验 url 是否以 http 或 https 开头if (!Security.isHttp(url)) {log.error("[HTTPURLConnection] 非法的 url 协议:" + url);return "不允许非http/https协议!!!";}// 解析 url 为 IP 地址String ip = Security.urltoIp(url);log.info("[HTTPURLConnection] SSRF解析IP:" + ip);// 校验 IP 是否为内网地址if (Security.isIntranet(ip)) {log.error("[HTTPURLConnection] 不允许访问内网:" + ip);return "不允许访问内网!!!";}try {return HttpClientUtils.HTTPURLConnection(url);} catch (Exception e) {log.error("[HTTPURLConnection] 访问失败:" + e.getMessage());return "访问失败,请稍后再试!!!";}
}// 1. 判断是否为http协议
public static boolean isHttp(String url) {return url.startsWith("http://") || url.startsWith("https://");
}// 2. 解析IP地址
public static String urltoIp(String url) {try {URI uri = new URI(url);String host = uri.getHost().toLowerCase();InetAddress ip = Inet4Address.getByName(host);return ip.getHostAddress();} catch (Exception e) {return "127.0.0.1";}
}// 3. 判断是否为内网IP
public static boolean isIntranet(String url) {Pattern reg = Pattern.compile("^(127\\.0\\.0\\.1)|(localhost)|^(10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|^(172\\.((1[6-9])|(2\\d)|(3[01]))\\.\\d{1,3}\\.\\d{1,3})|^(192\\.168\\.\\d{1,3}\\.\\d{1,3})$");Matcher match = reg.matcher(url);Boolean a = match.find();return a;
}// 4. 不允许302跳转
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setInstanceFollowRedirects(false); // 不允许重定向或者对重定向后的地址做二次判断
conn.connect();
}

(六)RCE(远程代码执行):

(1) ProcessBuilder方式导致RCE

对攻击者输入未作过滤,攻击者使用管道符进行RCE:
image

正常输入:filepath = "/path/to/file"
执行命令:sh -c "ls -l /path/to/file"

管道符拼接后:filepath = "/path/to/file; whoami"
执行命令:sh -c "ls -l /path/to/file; whoami"

可以采用黑名单的方式进行修复,将管道符全部过滤:

public static boolean checkOs(String content) {String[] black_list = {"|", ",", "&", "&&", ";", "||"};for (String s : black_list) {if (content.contains(s)) {return true;}}return false;
}

(2) Runtime.getRuntime().exec(); 的不当使用导致RCE:

Runtime.getRuntime().exec(cmd)中,会执行cmd参数中的命令,若cmd可控且未进行过滤处理,回导致RCE。
image

cmd /c start calc 命令弹出计算器:
image

可以通过设置白名单的方式,严格过滤用户输入的命令,防止RCE:

// 使用白名单替换黑名单。黑名单需要不断更新,而白名单只需要指定允许执行的命令,更容易维护。public static String safe(String cmd) {// 定义命令白名单Set<String> commands = new HashSet<\>();commands.add("ls");commands.add("pwd");// 检查用户提供的命令是否在白名单中String command = cmd.split("\\s+")[0];if (!commands.contains(command)) {return "命令不在白名单中";}...
}

用户只允许输入并执行白名单中指定的命令,禁止恶意命令。

(3) ProcessImpl类中的start()方法造成RCE:

ProcessImpl 是更为底层的实现,Runtime和ProcessBuilder执行命令实际上也是调用了ProcessImpl这个类,ProcessImpl 类是一个抽象类不能直接调用,但可以通过反射来间接调用ProcessImpl来达到执行命令的目的。
image

但代码中未对 cmd参数值进行过滤操作,导致可能会执行恶意代码造成RCE。修复方式同上,采用白名单对用户输入的值进行严格限制。

(4) 脚本引擎(ScriptEngine)代码注入:

image

load()函数用户从指定的URL处加载并执行 Javascript 脚本,若 某URL-> http://hacker.com/xxx.js 中的JS代码为恶意代码,被 eval()函数执行后,造成RCE。

可以采用白名单的方式进行修复,先验证用户输入的 URL 是否在白名单中,若在,则加载JS代码,若不在,抛出异常:

private static final Pattern ALLOWED_URL_PATTERN = Pattern.compile("^https?://(localhost|127\\.0\\.0\\.1|example\\.com)/.*$");public void jsEngine(String url) throws Exception {// 验证 URL 是否可信if (!ALLOWED_URL_PATTERN.matcher(url).matches()) {throw new IllegalArgumentException("Invalid URL: " + url);}

(5) Groovy使用不当造成RCE:

image

使用 GroovyShell中的 evaluate()方法进行命令执行,本质上依然是未对 cmd参数进行过滤操作,同理采用白名单的方式进行修复即可。

(七) Java反序列化漏洞:

(1) 重写ObjectInputStream对象的readObject()方法不当造成反序列化漏洞:

许多类在实现 Serializable 接口时,会对 readObject()方法进行重写来执行自定义的反序列化逻辑。比如攻击者构造了一个类名为 MaliciousClass,在这个类中,攻击者对 readObject()方法进行了重写,重写的 readObject方法中会执行恶意代码。当 MaliciousClass类被反序列化时,类中的 readObject()方法被调用,导致恶意代码的执行。

import java.io.*;public class MaliciousClass implements Serializable {private static final long serialVersionUID = 1L;private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {// 恶意代码Runtime.getRuntime().exec("rm -rf /");}
}

image

反序列化目标的值用户可控,导致可以对任意类进行反序列化操作,攻击者构造一个恶意类进行反序列化,就可以实现RCE。

可以使用限定用户只能对指定类进行反序列化的方式来预防:

// 使用Apache Commons IO的ValidatingObjectInputStream,accept方法来实现反序列化类白/黑名单控制public String safe(String base64) {try {base64 = base64.replace(" ", "+");byte[] bytes = Base64.getDecoder().decode(base64);ByteArrayInputStream stream = new ByteArrayInputStream(bytes);ValidatingObjectInputStream ois = new ValidatingObjectInputStream(stream);// 只允许反序列化Student classois.accept(Student.class);ois.readObject();return "ValidatingObjectInputStream";} catch (Exception e) {return e.toString();
}

通过黑/白名单的方式,只允许反序列化 Student 等合法类。

(2) yaml.load()使用不当造成反序列化

org.yaml.snakeyaml.Yaml 为 SnakeYAML的主类,用于解析和生成YAML格式的文档。类SnakeYAML中的load方法会将 YAML文档中的数据反序列化为 Java对象,若 YAML 文档中的内容包含恶意构造的数据,则会造成反序列化漏洞。

image

未对 yaml.load()函数的 content参数进行严格的过滤,导致产生反序列化漏洞。

image

可以使用 SafeConstructor来进行防御。SafeConstructor 是 SnakeYAML 提供的一个安全的构造器类,它限制了可以反序列化的类。默认情况下,SafeConstructor 只允许反序列化基本类型(如字符串、数字、布尔值)、集合类型(如列表和映射)以及其他一些安全的类。这样可以防止攻击者通过恶意构造的 YAML 数据来执行任意代码。

// SafeConstructor 是 SnakeYaml 提供的一个安全的构造器。它可以用来构造安全的对象,避免反序列化漏洞的发生。public void safe(String content) {Yaml y = new Yaml(new SafeConstructor());y.load(content);log.info("[safe] SnakeYaml反序列化: " + content);
}

SafeConstructor只允许反序列化一些基本类型的类,和一些自定义添加的安全类。添加自定义的安全类,比如Student类,代码如下所示:

public class CustomSafeConstructor extends SafeConstructor {public CustomSafeConstructor() {this.yamlConstructors.put(new Tag("!student"), new ConstructStudent());}private class ConstructStudent extends AbstractConstruct {@Overridepublic Object construct(Node node) {Map<String, Object> values = (Map<String, Object>) constructMapping(node);String name = (String) values.get("name");int age = (int) values.get("age");return new Student(name, age);}}
}public class Student {xxxxxxxxx  //get、set方法;name、age参数等
}

(3) XMLDecoder解析XML文件造成反序列化漏洞:

image

XMLDecoder中的readObject方法在反序列化对象时,会执行XML文件中的恶意代码,导致产生反序列化漏洞,造成RCE。

包含恶意代码的XML文件可以如下所示,调用ProcessBuilder类中的 start()方法来执行恶意代码:

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_231" class="java.beans.XMLDecoder"><object class="java.lang.ProcessBuilder"><array class="java.lang.String" length="3"><void index="0"><string>rm</string></void><void index="1"><string>-rf</string></void><void index="2"><string>/</string></void></array><void method="start"/>  //调用 ProcessBuilder类中的 start()方法,实现命令执行</object>
</java>

(八) XXE漏洞:

漏洞触发点就在XML解析时,因此重点审计XML解析器是否设置了相关的安全属性。

(1) XMLReader (org.xml.sax.XMLReader)

image

攻击者可以通过构造一个包含恶意外部实体的XML文档,达到任意文件读取、内网端口探测等恶意目的。

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [<!ENTITY xxe SYSTEM "http://dnslog.cn">]><root>&xxe;</root>

修复XXE漏洞,可以规定在使用XML解析器时,禁止使用外部实体:

public String XMLReader(@RequestBody String content) {try {XMLReader xmlReader = XMLReaderFactory.createXMLReader();// 修复:禁用外部实体xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);xmlReader.parse(new InputSource(new StringReader(content)));return "XMLReader XXE";} catch (Exception e) {return e.toString();}
}

(2) SAXReader (org.dom4j.io.SAXReader):

SAXReader是Apache dom4j库中的一个类,用于解析XML数据。
image

未禁用外部实体解析导致产生XXE。

(3) SAXBuilder (org.jdom.input.SAXBuilder):

SAXBuilder 是 JDOM 库中的一个类,用于解析 XML 数据。
image

未禁用外部实体解析导致产生XXE。

(4) DocumentBuilder (DocumentBuilder):

DocumentBuilder 是 Java 的 javax.xml.parsers 包中的一个类,用于解析 XML 文档并将其转换为 DOM 树。
image

未禁用外部实体解析导致产生XXE。

(5) Unmarshaller (javax.xml.bind.Unmarshaller):

Unmarshaller 是 Java Architecture for XML Binding (JAXB) 的核心类之一,用于将 XML 数据转换为 Java 对象。
image

未禁用外部实体解析导致产生XXE。

(九) 越权漏洞:

(1) 水平越权

水平越权的产生通常是由于后端未进行权限校验导致的。
image

可以采用使用 session、token等方式进行防御,简易的防御代码如下所示:

// 判断当前session与要查询的用户是否一致
public String safe(String name, HttpSession session) {if (session.getAttribute("LoginUser").equals(name)) {return userMapper.queryByUser(name);}
}

image

(4) 垂直越权:

垂直越权的产生是 admin 和 user的权限未作区分,导致普通用户可以向上垂直越权使用管理员的功能:
image

同理进行权限校验即可:

// 只允许admin用户可以访问管理页面
public String safe(HttpSession session) {if (session.getAttribute("LoginUser").equals("admin")) {return "admin";} else {return "commons/403";}
}

(十) 未授权访问:

拦截器中没有对未授权路径进行拦截导致未授权访问:
image

拦截未授权路径即可。

(十一) JNDI注入:

image

攻击者可以通过构造特定的 content 参数,利用 JNDI 查找功能来加载远程恶意代码,从而在服务器上执行任意代码。这通常通过 RMI 或 LDAP 协议实现。

可采用白名单的方式进行修复,限制 lookup方法只能查找预定义的、安全的资源:

public String safe2(String content) {List<String> whiteList = Arrays.asList("java:comp/env/jdbc/mydb", "java:comp/env/mail/mymail");if (whiteList.contains(content)) {try {Context ctx = new InitialContext();ctx.lookup(content);} catch (Exception e) {log.warn("JNDI错误消息");}return HtmlUtils.htmlEscape(content);  //返回经过转义的 content,也有效防止了XSS攻击} else {return "JNDI 白名单拦截";}
}

(十三) CSRF漏洞:

CSRF漏洞的产生大多是因为未校验 csrf_token 和 Referer头导致。
image

image

若对 csrf_token 和 Referer头进行校验,就可以很好的防御CSRF漏洞:
(1) 校验csrf_token:

 String token = request.getParameter("csrfToken");String sessionToken = (String) session.getAttribute("csrfToken");// 校验CSRF Tokenif (!token.equals(sessionToken)) {result.put("success", false);result.put("message", "token is not valid");return result;}

(2) 校验 Referer头:

 String token = request.getParameter("csrfToken");String sessionToken = (String) session.getAttribute("csrfToken");// 校验CSRF Tokenif (!token.equals(sessionToken)) {result.put("success", false);result.put("message", "token is not valid");return result;}

(十四) URL重定向漏洞:

利用URL重定向中的被信任域名来钓鱼,关键参数:url、redirectURL、target等。
image

采用白名单进行防御,只允许跳转到白名单中设置的安全地址:

public static boolean isWhite(String url) {List<String> url_list = new ArrayList<String>();url_list.add("baidu.com");url_list.add("www.baidu.com");url_list.add("oa.baidu.com");URI uri = null;try {uri = new URI(url);} catch (URISyntaxException e) {System.out.print(e);}String host = uri.getHost().toLowerCase();System.out.println(host);return url_list.contains(host);}

(十五) DOS(拒绝服务)漏洞:

漏洞代码:

public String vul(String content) {boolean match = Pattern.matches("(a|aa)+", content);return String.format("正则匹配:%s,正则表达式拒绝服务攻击", match);
}

安全代码:

public String safe(String content) {boolean match = com.google.re2j.Pattern.matches("(a|aa)+", content);return String.format("正则匹配:%s,安全正则表达式", match);
}

若 content的值很长,很复杂,漏洞代码会出现DOS漏洞。

原因:

漏洞代码中进行正则匹配使用的是 Java内置的正则表达式引擎->java.util.regex.Pattern。在处理某些特定的输入时,可能会导致指数级的时间复杂度,从而引发 ReDoS 攻击。例如,对于正则表达式 (a|aa)+,输入 aaaaaaaaaaaaaaaaaaaaaaa! 会导致引擎花费大量时间进行匹配。

而安全代码中则使用了 RE2 正则表达式引擎->com.google.re2j.Pattern。设计上避免了回溯,因此在处理复杂正则表达式时具有线性的时间复杂度->O(n),不会引发 ReDoS 攻击。

(十六) Swagger未授权访问:

修复很简单->限制访问环境即可:

@Configuration
@EnableSwagger2
// 设置swagger只能在dev和test环境可访问
@Profile({"dev", "test"})
public class Swagger2Config {}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/69793.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

记录一次本地安装AI ollama大模型数据对话 的经历

浏览器打开 Ollama官网 下载对应的版本,我这里下载的 是对应 windows的版本,下载后直接运行安装安装完成后 打开 dos控制台,win+r,cmd那个,输入ollama 如果显示如下截图内容,就说明安装成功了,接下来就是下载 具体的 大数据库了 安装大模型前,建议先修改环境变量,因…

APP应用分发多个步骤和策略过程,如何进行app应用分发?

进行APP应用分发是一个涉及多个步骤和策略的过程,以下是对该过程的详细解析: 一、前期准备应用程序准备:开发人员需要确保应用程序已经经过完整的测试和质量保证,包括功能测试、用户体验测试、性能测试等,以确保其稳定性和可靠性。 打包应用程序,将开发完成的APP进行编译…

利用 ACME 实现SSL证书自动化配置更新

SSL 证书自动化最近收到腾讯云的通知SSL证书要到期了,本想直接申请的发现现在申请的免费SSL证书有效期只有90天了,顺便了解了一下原因是包括Google在内的国际顶级科技公司一直都有在推进免费证书90天有效期的建议,免费证书加密等级低,难以应对今天日益复杂的网络环境,90天…

arm imx6ull docker启动失败问题查找与解决 内核配置相关

arm imx6ull docker启动失败问题查找与解决 内核配置相关1、增加POSIX Message qeue:could not get initial namespace: no such file or directory CONFIG_POSIX_MQUEUE=y 2、增加namespace failed to set to initial namespace CONFIG_NAMESPACES=y 3、创建网络失败,veth配…

ubuntu的镜像源+bionic版本

首先第一步 查找和你自己ubuntu版本匹配的版本号 匹配代号如下在终端输入lsb_release -a查看自己系统上的版本号可以看到我这个版本号的代号是bionic。 每个版本的镜像文件都是有规律的。 bionic版本的源如下:点击查看代码 # 阿里源 deb http://mirrors.aliyun.com/ubuntu/ bi…

PictureBox实现进入换色,离开换色,点击换色

实现和Word标题栏类似的效果可以看到有三种颜色: 默认时是(243, 243, 243),鼠标进入时是这样(210, 210, 210),鼠标按下的瞬间变为了(177, 177, 177) 4个关键事件:MouseEnter、MouseLeave、MouseDown、MouseUp MouseEnter:在鼠标进入控件的可见部分时发生private void pictu…

Spring事务的1道面试题

每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。 原理 Spring事务的原理是:通过AOP切面的方…

安装 Anaconda、PyTorch(GPU 版)库与 PyCharm

Anaconda 是一款巨大的 Python 环境集成平台,里面包含了 Python 解释器、Jupyter Notebook 代码编辑器以及很多的第三方库,所以安装 Anaconda 后我们无需再安装 Python 解释器,非常方便。 一、安装 Anaconda 1.卸载 Anaconda(可选) 如果我们原来的电脑上安装过 Anaconda,…