代码审计实战3-android java

news2024/11/13 20:36:09

jks

java keystore

作用:保证应用的唯一性

简介:可以理解为java的密钥库,是一个用来存放密钥和证书的仓库。
(而keytool就是密钥和证书的管理工具,它把key(密钥)和certificate(证书)存放在一个叫keystore的文件中)

jks可以同时容纳n个公钥或私钥,后缀一般是.jks或者.keystore或.truststore等,千奇百怪
用jdk\bin目录下的keytool.exe对其进行查看,导入,导出,删除,修改密码等各种操作。

使用举例:Android Studio生成.jks文件


使用举例:生成jks证书文件:确保安装了JDK,在命令行中输入如下命令

keytool.exe -genkeypair -alias filename -keyalg RSA -keypass 501937 -storepass 501937 -keyalg RSA -keysize 2048 -validity 3650 -keystore filename.jks
您的名字与姓氏是什么?
[Unknown]:  cheng
您的组织单位名称是什么?
[Unknown]:  wang
您的组织名称是什么?
[Unknown]:  qiu
您所在的城市或区域名称是什么?
[Unknown]:  shanghai
您所在的省//自治区名称是什么?
[Unknown]:  shanghai
该单位的双字母国家/地区代码是什么?
[Unknown]:  CN
CN=cheng, OU=wang, O=qiu, L=shanghai, ST=shanghai, C=CN是否正确?
[]: y

Warning:                                        
JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore filename.jks -destkeystore filename.jks -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。

keytool -importkeystore -srckeystore filename.jks -destkeystore filename.jks -deststoretype pkcs12
输入源密钥库口令:
已成功导入别名 filename 的条目。
已完成导入命令: 1 个条目成功导入, 0 个条目失败或取消

Warning:
已将 "filename.jks" 迁移到 Non JKS/JCEKS。将 JKS 密钥库作为 "filename.jks.old" 进行了备份。

注:
storepass keystore 文件存储密码
keypass 私钥加解密密码
PS: 上面两个密码要一致
keyalt 采用公钥算法,默认是DSA
validity 有效期 单位是天
keysize 密钥长度(DSA算法对应的默认算法是sha1withDSA,不支持2048长度,此时需指定RSA)
keystore 指定keystore文件 如上面命令中filename.jks

签名文件kestore和jks:
在作用上基本上没有太大区别,主要是生成来源不一样,它们是由不同的IDE生成,
keystore文件一般是由Eclipce或dos命令行生成,而jks一般是在Android studio上自动生成。

要实现这个两种签名文件的相互转化,需要用到一个工具:keytool。
在这里插入图片描述

注意到这个知识点是因为,我在build.gradle文件中看到

signingConfigs {
    release {
      storeFile file("../myapp.jks")
      storePassword"mypassword"
      keyAlias"My_App"
      keyPassword"mykeypassword"
    }
    debug {
      storeFile file('../debug.keystore')
      storePassword 'android'
      keyAlias 'androiddebugkey'
      keyPassword 'android'
    }
  }

类似这种,我以为是硬编码

关于编码:保护Android Java代码中的硬编码值(API密钥):https://www.codenong.com/35378295/

但其实将密钥添加到build.gradle文件中。 这些文件不包含在您的apk中。 实际上,这些只是用来对您的应用程序进行签名并创建发行版本。

不算硬编码。
那可以算弱口令吗?

APP客户端与后台服务端通信

https://www.bbsmax.com/A/x9J2kbXZ56/

AIDL(Android Interface definition language-“接口定义语言”) 是 Android 提供的一种进程间通信 (IPC:Inter-Process Communication) 机制,支持的数据类型:

  1. Java 的原生类型;
  2. String 和CharSequence;
  3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型; 以上三种类型都不需要导入(import);
  4. AIDL 自动生成的接口 需要导入(import);
  5. 实现android.os.Parcelable 接口的类. 需要导入(import)。

加密

加密算法:https://www.pianshen.com/article/4442439864/
https://blog.csdn.net/xihuailu3244/article/details/109769037
https://www.cnblogs.com/whoislcj/p/5580950.html
http://www.360doc.com/content/23/0210/12/81615618_1067007071.shtml
密钥常量:https://www.jianshu.com/p/f366b7c115a5
为什么我们应该编写所有这些步骤来使用java生成DES算法的密钥https://www.orcode.com/question/1320303_k56849.html
https://blog.csdn.net/mlymark/article/details/49175789
https://www.orcode.com/question/1320303_k56849.html
Android应用安全开发之浅谈硬编码: https://www.pianshen.com/article/9026439286/
https://blog.csdn.net/mlymark/article/details/49175789

Intent Scheme URLs攻击

Environment.getExternalStorageDirectory()

Android SDK 版本超过29编译的时候,Android Studio会提示Environment.getExternalStorageDirectory()过时了,要用Context#getExternalFilesDir代替,

Android Q以后Environment.getExternalStorageDirectory()返回的路径可能无法直接访问,所以改成了Context#getExternalFilesDir

android:exported=“true”

android:exported 是描述四大组件的参数,表示当前组件能否被其他应用程序的组件调用或与之交互。

android:exported 为true,表示可以和其他应用程序的组件发生调用和交互。
android:exported 为false,表示只能当前应用程序组件或者带有相同的用户ID(shareUserId)的应用程序调用和交互。

exported属性的默认值四大组件略有不同:

  • Activity/Service/Receiver:当没有设置 intent-filter时 ,默认false。
  • Provider:当Android sdk版本为16或更低版本时,默认值为true,如果是17及以上版本则默认为false。

设置intent-filter的场景

        <service
            android:name="com.alibaba.sdk.android.push.MsgService">
            <intent-filter>
                <action android:name="com.alibaba.sdk.android.push.NOTIFY_ACTION" />
            </intent-filter>
        </service>

此情况下说明,除了“内部使用”外,组件需要被部分“特定”App调用的,推荐
1、调用的App与当前暴露组件的App使用同一uid:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         ...
          android:sharedUserId="xxx.xxx.xxx">

2、或通过对暴露的组件设置permission:

<activity android:name=".xxxActivity"
          android:label="自定义permission"
          android:permission="com.xxx.permission" >
 
    <intent-filter>
        <action android:name="android.xxx.action" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

既可满足暴露组件的需求,又可以保护数据安全。

https://blog.csdn.net/weixin_35691921/article/details/120430987

Intent(意图)

负责完成Android应用、组件之间的交互与通信。
常见的Activity的调用、Receiver的发送、Service的启动都需离不开Intent。

Intent通常包含的信息:
Categpry:种类、归类。
Action:表明要做什么?通常代表了一个组件具有的能力。
Data/Extras:通信的数据。
Flags:规定了系统如何去启动一个Activity。

Activity(活动)

通常应用里一个显示界面就是一个Activity,Activity负责显示界面的元素和与用户之间的交互。

https://blog.csdn.net/wsq_tomato/article/details/83349812
https://blog.csdn.net/qy1387/article/details/73740438
https://zhuanlan.zhihu.com/p/554069141

WebView

https://blog.csdn.net/qq_35114086/article/details/88796144/
https://blog.csdn.net/weixin_35691921/article/details/120430987
https://code84.com/750978.html
https://www.jianshu.com/p/6e9695fdedca

Android WebView在Android平台上是一个特殊的View,它能用来显示网页,这个WebView类可以被用来在app中仅仅显示一张在线的网页,还可以用来开发浏览器。
WebView内部实现是采用渲染引擎(WebKit)来展示view的内容,提供网页前进后退、网页放大、缩小、搜索等功能。Android WebView 在低版本和高版本采用了不同的 webkit 版本内核,在 4.4 版本后使用 Chrome 内核。

作用:

  • 显示和渲染Web页面
  • 直接使用html文件(网络上或本地assets中)作布局
  • 可和JavaScript交互调用

WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理。

使用:

1、本地加载
在布局文件中来添加WebView控件
在这里插入图片描述
在代码中让WebView控件加载显示网页
在这里插入图片描述
在配置文件中添加网络权限:

<!-- 添加网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />

运行效果
在这里插入图片描述
2、远程加载
在本地桌面新建js文件attack.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Carson</title>
    <script>
         function callAndroid(){
            //由于对象映射,所以调用test对象等于调用Android映射的对象
            test.hello("WindXaa!");
         }
</script>
</head>
<body>
   <!--点击按钮则调用callAndroid函数-->
   <button type="button" id="button1" onclick="callAndroid()">Internet Click connect</button>
</body>
</html>

开启一个简易的http_server的监听
在这里插入图片描述
编写代码:

{
WebView mWebView = (WebView) findViewById(R.id.Wind_webview1);
WebSettings webSettings = mWebView.getSettings();// 设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);// 通过addJavascriptInterface()将Java对象映射到JS对象
//参数1:Javascript对象名
//参数2:Java对象名
mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS类对象映射到js的test对象
mWebView.loadData("","text/html",null);
// 加载JS代码
// 格式规定为:file:///android_asset/文件名.html
// mWebView.loadUrl("file:///android_asset/javascript.html");
mWebView.loadUrl("http://ip地址填自己的/attack.html");
}/**
 * 提供接口在Webview中供JS调用
 */
public class AndroidtoJs {
    // 定义JS需要调用的方法,被JS调用的方法必须加入@JavascriptInterface注解
    @JavascriptInterface
    public void hello(String msg) {
        Log.e("WindXaa","Hello," + msg);
    }
}

运行效果:
点击按钮,成功的通过JS调用Android代码
在这里插入图片描述
WebView常用方法

webView.onResume();// 激活WebView为活跃状态,能正常执行网页的响应
webView.onPause();// 当页面被失去焦点被切换到后台不可见状态,需要执行onPause
// 通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
webView.pauseTimers()// 当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
// 它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
webView.resumeTimers()// 恢复pauseTimers状态

rootLayout.removeView(webView)
webView.destory()
// webview调用destory时,webview仍绑定在Activity上
// 需要先从父容器中移除webview,然后再销毁webview

前进、后退网页

Webview.canGoBack()//是否可以后退
Webview.goBack()//后退网页
Webview.canGoForward()//是否可以前进
Webview.goForward()//前进网页
//以当前的index为起始点前进或者后退到历史记录中指定的steps
//如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(intsteps)

在不做任何处理前提下,浏览网页时点击系统的“Back”键时,整个 Browser 会调用 finish()而结束自身,因此需要在当前Activity中处理并消费掉该 Back 事件,当按下返回键时,调用goBack方法。
我们可以做一些处理,让点击“Back”键后,让网页返回上一页而不是直接退出浏览器,此时我们可以在当前的Activity中处理Back事件,如下:
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) {
         mWebView.goBack();return true;}return super.onKeyDown(keyCode, event);}

常用类

WebSettings类:对WebView进行配置和管理

配置步骤:
第一步:添加访问网络权限(AndroidManifest.xml)
<uses-permission android:name="android.permission.INTERNET"/>

注意:从Android 9.0(API级别28)开始,默认情况下禁用明文支持,会显示 ERR_CLEARTEXT_NOT_PERMITTED。
因此http的url均无法在webview中加载,可以在manifest中application节点添加android:usesCleartextTraffic="true"。

第二步:生成一个WebView组件(有两种方式)
//方式1:直接在在Activity中生成
WebView webView = new WebView(this)
//方法2:在Activity的layout文件里添加webview控件:
WebView webview = (WebView) findViewById(R.id.webView1);

第三步:进行配置-利用WebSettings子类(常见方法)
WebSettings webSettings = webView.getSettings();//声明WebSettings子类
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);
//支持插件
webSettings.setPluginsEnabled(true);//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小//缩放操作
webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件//其他细节操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
webSettings.setAllowFileAccess(true); //设置可以访问文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式

常见方法:设置WebView缓存

当加载 html 页面时,WebView会在/data/data/包名目录下生成 database 与 cache 两个文件夹
请求的 URL记录保存在 WebViewCache.db,而 URL的内容是保存在 WebViewCache 文件夹下
是否启用缓存:
//优先使用缓存
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//缓存模式如下:
    //LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
    //LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
    //LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
    //LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据//不使用缓存
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
WebViewClient类:用来处理各种通知和请求事件

shouldOverrideUrlLoading():打开网页时不调用系统浏览器, 而是在本WebView中显示;在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。

//Webview控件
Webview webview = (WebView) findViewById(R.id.webView);
//加载一个网页
webView.loadUrl("http://www.google.com/");
//重写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示
webView.setWebViewClient(new WebViewClient(){
     @Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {
           view.loadUrl(url);return true;}});

onPageStarted()作用:开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。

webView.setWebViewClient(new WebViewClient(){
     @Override
     public void  onPageStarted(WebView view, String url, Bitmap favicon) {
        //设定加载开始的操作
     }
 });

onLoadResource()作用:在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。

webView.setWebViewClient(new WebViewClient(){
      @Override
      public boolean onLoadResource(WebView view, String url) {
         //设定加载资源的操作
      }
  });

onReceivedError()作用:加载页面的服务器出现错误时(如404)调用。

//App里面使用webview控件的时候遇到了诸如404这类的错误的时候,若也显示浏览器里面的那种错误提示页面就显得很丑陋了,那么这个时候我们的app就需要加载一个本地的错误提示页面,即webview如何加载一个本地的页面。

//步骤1:写一个html文件(error_handle.html),用于出错时展示给用户看的提示页面
//步骤2:将该html文件放置到代码根目录的assets文件夹下
//步骤3:复写WebViewClient的onRecievedError方法
//该方法传回了错误码,根据错误类型可以进行不同的错误分类处理
    webView.setWebViewClient(new WebViewClient(){
      @Override
      public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
switch(errorCode)
                {
                case HttpStatus.SC_NOT_FOUND:
                    view.loadUrl("file:///android_assets/error_handle.html");
                    break;
                }
            }
        });

onReceivedSslError():处理https请求
webView默认是不处理https请求的,页面显示空白,需要进行如下设置:

webView.setWebViewClient(new WebViewClient() {   
        @Override   
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {   
            handler.proceed();    //表示等待证书响应
        // handler.cancel();      //表示挂起连接,为默认方式
        // handler.handleMessage(null);    //可做其他处理
        }   
    });

c.WebChromeClient:辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。


onProgressChanged():获得网页的加载进度并显示

webview.setWebChromeClient(new WebChromeClient(){@Override
      public void onProgressChanged(WebView view, int newProgress) {
          if (newProgress < 100) {
              String progress = newProgress + "%";
              progress.setText(progress);
            } else {
        }
    });

onReceivedTitle():获取Web页中的标题每个网页的页面都有一个标题,比如http://www.baidu.com这个页面的标题即“百度一下,你就知道”,那么如何知道当前webview正在加载的页面的title并进行设置呢?

webview.setWebChromeClient(new WebChromeClient(){
    @Override
    public void onReceivedTitle(WebView view, String title) {
       titleview.setText(title)}

Android WebView与JS的交互

https://mp.weixin.qq.com/s/KcmhXCIE8cr32OVBVkc4Lg

在这里插入图片描述

安全风险

webview.getSettings().setJavaScriptEnabled(true)
设置WebView是否允许执行JavaScript脚本,默认false。

当targetSdkVersion小于17时,攻击者可以利用addJavascriptInterface这个接口添加的函数远程执行任意代码

解决办法:

  • API17 (Android 4.2) 版本之后,需要在被调用的地方加上 @addJavascriptInterface 约束注解,因为不加上注解的方法是没有办法被调用的。进而避免漏洞攻击。
  • API < 17:需要采用拦截prompt()的方式进行漏洞修复。

https://blog.csdn.net/weixin_32534669/article/details/117833703

https

https://blog.51cto.com/u_4029519/5425558
https://blog.csdn.net/caizehui/article/details/112464577
https://mp.weixin.qq.com/s?__biz=MzIyNTgwOTIyOQ==&mid=2247489201&idx=1&sn=3a93ec381c40e7497bed2ad5196bcf10&chksm=e87b5093df0cd9858866724530f94427a653f1921b951fac36104352764f9d912b079e289a3d&scene=27

存储

https://blog.csdn.net/libo407/article/details/129862032?ydreferer=aHR0cHM6Ly93d3cuYmFpZHUuY29tL2xpbms%2FdXJsPTYzU2RQOU03ejRSS0xWSWpueE5NY185RS1WZzFmMUlQUExfQWUzUVBiUFBNQU9FOTBkSDFERGN3Qkp3bWduaHlaa1B6S1NITHpqcFN4d1R3Q2pKZlhxNHByTDFEbUlZN1JYWUczaTZrcHZPJndkPSZlcWlkPWFiNjhjZmM0MDAwMDQxNDkwMDAwMDAwNDY0MzY1YTYz
https://blog.csdn.net/qq_20466945/article/details/80199252
https://blog.csdn.net/Ananas_Orangey/article/details/126219957
https://www.jb51.net/article/220703.htm

审计参考

https://blog.51cto.com/u_14397532/3000922
https://mp.weixin.qq.com/s?__biz=MzIyNTgwOTIyOQ==&mid=2247489201&idx=1&sn=3a93ec381c40e7497bed2ad5196bcf10&chksm=e87b5093df0cd9858866724530f94427a653f1921b951fac36104352764f9d912b079e289a3d&scene=27
https://www.wangan.com/p/7fy7f882627f2fdb#8.1谨慎使用高风险函数

代码审计2-java

@PreAuthorize

@PreAuthorize("@ss.hasPermi('show:type:list')"):用于权限控制,只有拥有’show:type:list’权限的用户才能访问这个接口

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

作用在代码的注解是:
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。

作用在其他注解的注解(或者说 元注解):
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

Annotation通用定义

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
}

(1)@interface
定义 Annotation 时,@interface 是必须的。
使用 @interface 定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。
(2)@Documented
类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。

定义 Annotation 时,@Documented 可有可无;若没有定义,则 Annotation 不会出现在 javadoc 中。

(3)@Target(ElementType.TYPE)
ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。

ElementType.METHOD:该注解只能声明在一个类的方法前。
ElementType.TYPE:该注解只能声明在一个类前。
(4)@Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME) 的意思就是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这就意味着,编译器会将该 Annotation 信息保留在 .class 文件中,并且能被虚拟机读取。

总结: @interface 用来声明 Annotation,@Documented 用来表示该 Annotation 是否会出现在 javadoc 中, @Target 用来指定 Annotation 的类型,@Retention 用来指定 Annotation 的策略。

Spring Security

Spring Security提供了Spring EL表达式,允许我们在定义接口访问的方法上面添加注解,来控制访问权限。

@PreAuthorize注解用于配置接口,要求用户拥有某些权限才可访问,它拥有如下方法
在这里插入图片描述
1、数据权限
整体逻辑:每个用户分配角色之后,都会有一定的权限,在用户访问某个接口时,会判断用户所拥有的权限是否包含该接口需要的权限,然后将判断的结果(“true"或者"fales”)传入@PreAuthorize注解实现数据权限的控制。当Spring EL 表达式返回TRUE,则权限校验通过。

示例:
在这里插入图片描述
@PreAuthorize注解括号里面接收的是String类型的参数,且值为 “true” 或者 “false”

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize {
    String value();
    }

ss.hasPermi() 方法:
ss:ss是PermissionService类实例化后起的的对象名

hasPermi() 方法接收一个 String类型的参数,值为’system:user:add’,返回值为布尔类型,首先进行校验,判断参数是否为空,然后拿到当前登录系统的用户,判断当前用户是否为空以及用户拥有的权限是否为空(调用 loginUser.getPermissions() 方法,返回一个Set集合的权限列表),最后调用 hasPermissions(loginUser.getPermissions(), permission) 方法判断用户是否有该权限,如果包含就返回 true。
1
在这里插入图片描述
2、角色权限
角色权限时根据用户所属的角色来进行权限控制,实际原理和数据权限一样。

// 属于user角色
@PreAuthorize("@ss.hasRole('user')")
 
// 不属于user角色
@PreAuthorize("@ss.lacksRole('user')")
 
// 属于user或者admin之一
@PreAuthorize("@ss.hasAnyRoles('user,admin')")

@Validated

@Valid和@Validated是Spring Validation框架提供的参数验证功能

二者都作为标准JSR-303规范。在检验Controller的入参是否符合规范时,使用@Validated或者@Valid在基本验证功能上没有太多区别。但是在分组、注解地方、嵌套验证等功能上两个有所不同:

@Valid:@Valid注解用于校验,所属包为:javax.validation.Valid。
用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

@Validated:@Validated是@Valid 的一次封装,是Spring提供的校验机制使用。
用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

-----引入并使用@Validated参数验证:

1、引入校验的依赖包

        <!--第一种方式导入校验依赖:使用springboot时,在org\springframework\spring-context\5.2.1.RELEASE\spring-context-5.2.1.RELEASE.jar-->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--第二种方式导入校验依赖-->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
        <!--第三种方式导入校验依赖-->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>

2、在实体类定义要校验的参数
在这里插入图片描述
3、在这里插入图片描述

javax.validation.constraints下参数条件注解详解

实现参数验证功能,我们需要@Validated注解配合 在实体类的的参数加上条件验证注解(设置具体的条件限制规则)一起实现参数验证功能。

而这些参数条件注解是由javax.validation.constraints包下提供,主要如下:

@NotNull :被注解的元素必须不为null
@NotBlank注解 :验证注解的元素值不为空(不为null、去除首位空格后长度为0) ,并且类型为String@NotEmpty注解 :验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0) ,并且类型为String@AssertTrue注解 :被注解的元素必须为true,并且类型为boolean@AssertFalse注解 :被注解的元素必须为false,并且类型为boolean@Min注解 :被注解的元素其值必须大于等于最小值,并且类型为intlongfloatdouble@Max注解:被注解的元素其值必须小于等于最小值,并且类型为intlongfloatdouble@DecimalMin注解 :验证注解的元素值大于等于@DecimalMin指定的value值,并且类型为BigDecimal@DecimalMax注解 :验证注解的元素值小于等于@DecimalMax指定的value值 ,并且类型为BigDecimal@Range注解 :验证注解的元素值在最小值和最大值之间,并且类型为BigDecimalBigIntegerCharSequencebyteshortintlong@Past注解 :被注解的元素必须为过去的一个时间,并且类型为java.util.Date@Future注解 :被注解的元素必须为未来的一个时间,并且类型为java.util.Date@Size注解 :被注解的元素的长度必须在指定范围内,并且类型为StringArrayListMap@Length注解 :验证注解的元素值长度在min和max区间内 ,并且类型为String@Digits注解 :验证注解的元素值的整数位数和小数位数上限 ,并且类型为floatdoubleBigDecimal@Pattern注解 :被注解的元素必须符合指定的正则表达式,并且类型为String@Email注解: 验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式,类型为String

自定义条件注解

如果在写项目的过程中,参数需要的条件注解满足不上,则我们需要自定义注解来完成

步骤:
1.创建一个自定义的注解类

//自定义条件注解
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
public @interface ListValue {
    //配置路径,后端传递信息
    String message() default "{com.itfuture.e.valid.ListValue.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    //自定义一个类型来存放数据(数组)
    int[] values() default {};
}

String message() default "{com.itfuture.e.valid.ListValue.message}";也可以通过配置文件去配置:
在这里插入图片描述

2、创建一个逻辑处理数据的方法

//自定义显示状态
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
    //set存储
    private Set<Integer> set = new HashSet<>();

    //初始化数据
    //listValue拿到的是注解中的数据
    @Override
    public void initialize(ListValue constraintAnnotation) {
        //拿到注解中自定义的数据,且是数组型的
        int[] values = constraintAnnotation.values();
        //放在数组里,遍历判断
        for(int value:values){
            set.add(value);
        }
    }

    //判断数据是否相同
    @Override
    public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
        if(set.contains(integer)){
            return true;
        }
        return false;
    }
}

3、在实体类的参数条件中来调用

在这里插入图片描述

spring-boot-devtools热部署

  • 是一个为开发者服务的模块,可以实现 Spring Boot 热部署,其中最重要的功能就是自动将应用代码更改到最新的 App 上面去。
  • 可以实现页面热部署,实现类文件热部署(类文件修改后不会立即生效),实现对属性配置文件的热部署。其原理是 spring-boot-devtools 会监听 Classpath 下的文件变动,并且会立即重启应用(发生在保存时机)。
  • 由于采用的虚拟机机制,重启的时候只是加载了在开发的 Class,没有重新加载第三方的 JAR 包,所以重启是很快的。

其深层原理是使用了两个ClassLoader,一个Classloader加载那些不会改变的类(第三方Jar包),另一个ClassLoader加载会更改的类,称为 restart ClassLoader ,这样在有代码更改的时候,原来的restart ClassLoader 被丢弃,重新创建一个restart ClassLoader,由于需要加载的类相比较少,所以实现了较快的重启时间

idea使用示例:
由于idea 没有保存修改,也就是说在idea中并不会因为ctrl+s 就重新编译代码。那么就需要额外的配置

1、在pom文件中,增加编译插件,让代码有变动的时候也编译

<!--热部署依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
 </dependency>

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <!--没有该项配置,热部署不会起作用-->
    <configuration>
        <fork>true</fork>
    </configuration>
 </plugin>

2、application.properties

# 页面修改后立即生效,关闭缓存,立即刷新
spring.thymeleaf.cache=false
# 热部署生效
spring.devtools.restart.enabled=true
# 设置需要重启的目录
spring.devtools.restart.additional-paths=src/main/java
# 设置不需要重启的目录
spring.devtools.restart.exclude=static/**,public/**,WEB-INF/**
# 为 mybatis 设置,生产环境可删除
# restart.include.mapper=/mapper-[\\w-\\.]+jar
# restart.include.pagehelper=/pagehelper-[\\w-\\.]+jar

3、设置 IDEA 环境自动编译
在编译器选项中勾选 Build project automatically 选项
在这里插入图片描述
在这里插入图片描述

FreeMarker模板注入

https://zhuanlan.zhihu.com/p/432361789

FreeMarker 是一款模板引擎,即一种基于模板和需要改变的数据, 并用来生成输出文本( HTML 网页,电子邮件,配置文件,源代码等)的通用工具,其模板语言为 FreeMarker Template Language (FTL),模板文件简称为FTL(后缀可能也为这个)。

它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件,适用于Web开发框架生成html页面。所以此类库经常应用于MVC开发模式的Java Web程序。
在这里插入图片描述
FreeMarker 模板文件与 HTML 一样都是静态页面,当用户访问页面时,FreeMarker 引擎会进行解析并动态替换模板中的内容进行渲染,然后将渲染后的结果返回到浏览器中。

FreeMarker 模板语言(FreeMarker Template Language,FTL)由 4 个部分组成:

  • 文本:包括HTML标签与静态文本等静态内容,会原样输出;
  • 插值:这部分的输出会被计算的数据来替换,使用${}这种语法;
  • 标签:给FreeMarker的指示,可以简单与指令等同,不会打印在内容中,比如<#assign name=‘bob’>;
  • 注释:由<#–和–>表示,不会被freemarker处理。

FreeMarker模版内容示例:

<html>
<head>
    <title>Welcome TeamsSix!</title>
</head>
<body> <#-- 这是注释 -->
<h1>Welcome !</h1>
<p>Our latest product:
    <a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>

1、漏洞原理:
服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

springboot防止XSS攻击和sql注入

https://blog.csdn.net/china_coding/article/details/129680240

①:创建Xss请求过滤类XssHttpServletRequestWraper
在这里插入图片描述

public class XssHttpServletRequestWraper extends HttpServletRequestWrapper {
 
    Logger log = LoggerFactory.getLogger(this.getClass());
 
 
    public XssHttpServletRequestWraper() {
        super(null);
    }
 
    public XssHttpServletRequestWraper(HttpServletRequest httpservletrequest) {
        super(httpservletrequest);
    }
 
	//过滤springmvc中的 @RequestParam 注解中的参数
    public String[] getParameterValues(String s) {
 
        String str[] = super.getParameterValues(s);
        if (str == null) {
            return null;
        }
        int i = str.length;
        String as1[] = new String[i];
        for (int j = 0; j < i; j++) {
            //System.out.println("getParameterValues:"+str[j]);
            as1[j] = cleanXSS(cleanSQLInject(str[j]));
        }
        log.info("XssHttpServletRequestWraper净化后的请求为:==========" + as1);
        return as1;
    }
 
	//过滤request.getParameter的参数
    public String getParameter(String s) {
        String s1 = super.getParameter(s);
        if (s1 == null) {
            return null;
        } else {
            String s2 = cleanXSS(cleanSQLInject(s1));
            log.info("XssHttpServletRequestWraper净化后的请求为:==========" + s2);
            return s2;
        }
    }
 
	//过滤请求体 json 格式的
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(inputHandlers(super.getInputStream ()).getBytes ());
 
        return new ServletInputStream() {
 
            @Override
            public int read() throws IOException {
                return bais.read();
            }
 
            @Override
            public boolean isFinished() {
                return false;
            }
 
            @Override
            public boolean isReady() {
                return false;
            }
 
            @Override
            public void setReadListener(ReadListener readListener) { }
        };
    }
 
	
    public   String inputHandlers(ServletInputStream servletInputStream){
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (servletInputStream != null) {
                try {
                    servletInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return  cleanXSS(sb.toString ());
    }
 
    public String cleanXSS(String src) {
        String temp = src;
 
        src = src.replaceAll("<", "<").replaceAll(">", ">");
        src = src.replaceAll("\\(", "(").replaceAll("\\)", ")");
        src = src.replaceAll("'", "'");
        src = src.replaceAll(";", ";");
        //bgh 2018/05/30  新增
        /**-----------------------start--------------------------*/
        src = src.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
        src = src.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41");
        src = src.replaceAll("eval\\((.*)\\)", "");
        src = src.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        src = src.replaceAll("script", "");
        src = src.replaceAll("link", "");
        src = src.replaceAll("frame", "");
        /**-----------------------end--------------------------*/
        Pattern pattern = Pattern.compile("(eval\\((.*)\\)|script)",
                Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(src);
        src = matcher.replaceAll("");
 
        pattern = Pattern.compile("[\\\"\\'][\\s]*javascript:(.*)[\\\"\\']",
                Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(src);
        src = matcher.replaceAll("\"\"");
 
        // 增加脚本
        src = src.replaceAll("script", "").replaceAll(";", "")
                /*.replaceAll("\"", "").replaceAll("@", "")*/
                .replaceAll("0x0d", "").replaceAll("0x0a", "");
 
        if (!temp.equals(src)) {
            // System.out.println("输入信息存在xss攻击!");
            // System.out.println("原始输入信息-->" + temp);
            // System.out.println("处理后信息-->" + src);
 
            log.error("xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!");
            log.error("原始输入信息-->" + temp);
 
            throw new CustomerException("xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!");
        }
        return src;
    }
 
    //输出
    public void outputMsgByOutputStream(HttpServletResponse response, String msg) throws IOException {
        ServletOutputStream outputStream = response.getOutputStream(); //获取输出流
        response.setHeader("content-type", "text/html;charset=UTF-8"); //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
        byte[] dataByteArr = msg.getBytes("UTF-8");// 将字符转换成字节数组,指定以UTF-8编码进行转换
        outputStream.write(dataByteArr);// 使用OutputStream流向客户端输出字节数组
    }
 
    // 需要增加通配,过滤大小写组合
    public String cleanSQLInject(String src) {
        String lowSrc = src.toLowerCase();
        String temp = src;
        String lowSrcAfter = lowSrc.replaceAll("insert", "forbidI")
                .replaceAll("select", "forbidS")
                .replaceAll("update", "forbidU")
                .replaceAll("delete", "forbidD").replaceAll("and", "forbidA")
                .replaceAll("or", "forbidO");
 
        if (!lowSrcAfter.equals(lowSrc)) {
            log.error("sql注入检查:输入信息存在SQL攻击!");
            log.error("原始输入信息-->" + temp);
            log.error("处理后信息-->" + lowSrc);
            throw new CustomerException("sql注入检查:参数含有非法攻击字符,已禁止继续访问!!");
 
        }
        return src;
    }
 
}

②:把请求过滤类XssHttpServletRequestWraper添加到Filter中,注入容器

@Component
public class XssFilter implements Filter {
 
    Logger log  = LoggerFactory.getLogger(this.getClass());
 
    // 忽略权限检查的url地址
    private final String[] excludeUrls = new String[]{
            "null"
    };
 
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
            throws IOException, ServletException {
 
        HttpServletRequest req = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;
 
        String pathInfo = req.getPathInfo() == null ? "" : req.getPathInfo();
        //获取请求url的后两层
        String url = req.getServletPath() + pathInfo;
        //获取请求你ip后的全部路径
        String uri = req.getRequestURI();
        //注入xss过滤器实例
        XssHttpServletRequestWraper reqW = new XssHttpServletRequestWraper(req);
 
        //过滤掉不需要的Xss校验的地址
        for (String str : excludeUrls) {
            if (uri.indexOf(str) >= 0) {
                arg2.doFilter(arg0, response);
                return;
            }
        }
        //过滤
        arg2.doFilter(reqW, response);
    }
    public void destroy() {
    }
    public void init(FilterConfig filterconfig1) throws ServletException {
    }
}

上述代码已经可以完成请求参数、JSON请求体的过滤,但对于json请求体还有其他的方式实现。

扩展:
还可以重写spring中的MappingJackson2HttpMessageConverter来过滤Json请求体

因为请求体在进出Contoroller时,会经过MappingJackson2HttpMessageConverter的一个转换,把请求体转换成我们需要的json格式,所以可以在这里边做一些修改!

@Configuration
public class MyConfiguration {
 
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
        //自定义转换器
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
 
 
        //转换器日期格式设置
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleDateFormat smt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        objectMapper.setDateFormat(smt);
        converter.setObjectMapper(objectMapper);
 
        //转换器添加自定义Module扩展,主要是在这里做XSS过滤的!!,其他的是其他业务,不用看
        SimpleModule simpleModule = new SimpleModule();
        //添加过滤逻辑类!
        simpleModule.addDeserializer(String.class,new StringDeserializer());
        converter.getObjectMapper().registerModule(simpleModule);
 
        //设置中文编码格式
        List<MediaType> list = new ArrayList<>();
        list.add(MediaType.APPLICATION_JSON_UTF8);
        converter.setSupportedMediaTypes(list);
 
        return converter;
    }
 
}

真正的过滤逻辑类StringDeserializer:

//检验请求体的参数
@Component
public class StringDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String str = jsonParser.getText().trim();
        //sql注入拦截
        if (sqlInject(str)) {
          throw new CustomerException("参数含有非法攻击字符,已禁止继续访问!");
        }
 
        return xssClean(str);
 
    }
 
    public boolean sqlInject(String str) {
 
        if (StringUtils.isEmpty(str)) {
            return false;
        }
 
        //去掉'|"|;|\字符
        str = org.apache.commons.lang3.StringUtils.replace(str, "'", "");
        str = org.apache.commons.lang3.StringUtils.replace(str, "\"", "");
        str = org.apache.commons.lang3.StringUtils.replace(str, ";", "");
        str = org.apache.commons.lang3.StringUtils.replace(str, "\\", "");
 
        //转换成小写
        str = str.toLowerCase();
 
        //非法字符
        String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alert","alter", "drop"};
 
        //判断是否包含非法字符
        for (String keyword : keywords) {
            if (str.indexOf(keyword) != -1) {
                return true;
            }
        }
        return false;
 
    }
 
    //xss攻击拦截
 
    public String xssClean(String value) {
        if (value == null || "".equals(value)) {
            return value;
        }
 
        //非法字符
        String[] keywords = {"<", ">", "<>", "()", ")", "(", "javascript:", "script","alter", "''","'"};
        //判断是否包含非法字符
        for (String keyword : keywords) {
            if (value.indexOf(keyword) != -1) {
               throw new CustomerException("参数含有非法攻击字符,已禁止继续访问!");
            }
        }
 
        return value;
    }
}

使用这种形式也可以完成json请求体的过滤,但个人更推荐使用XssHttpServletRequestWraper的形式来完成xss过滤

Mybatis 在 Mapper.xml 文件中的转义字符处理方式

在mybatis的映射文件写sql时,很多时候都需要写一些特殊的字符。例如:“<” 字符 “>” 字符 “>=” 字符 “<=” 字符,但是在xml文件中并不能直接写上述列举的字符,否则就会报错。
1、使用<![CDATA[ ]]>来解决

<select id = "selectUserByAge" resultType="com.test.hiioc.model.UserTable" >
    select
        id,userName,age
    from
        userTable
    <where>
        IS_DELETE = 1
        /*时间段查询*/
        <if test = "userTable.startDate != null">
            and SIGNING_DATE <![CDATA[>=]]> #{userTable.startDate}
        </if>
        <if test = "userTable.endDate!=null">
            and SIGNING_DATE <![CDATA[<=]]> #{userTable.endDate}
        </if>
    </where>
</select>

2、
在这里插入图片描述

<select id = "selectUserByAge" resultType="com.test.hiioc.model.UserTable" >
    select
        id,userName,age
    from
        userTable
    <where>
        IS_DELETE = 1
        /*时间段查询*/
        <if test = "userTable.startDate!=null">
            and SIGNING_DATE &gt;= #{userTable.startDate}
        </if>
        <if test = "userTable.endDate != null">
            and SIGNING_DATE &lt;= #{userTable.endDate}
        </if>
    </where>
</select>

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

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

相关文章

一零五六、Jsp+mysql 实现学生选课系统(附源码及数据库)

目录 实现效果 项目代码 数据库 结语 实现效果 login.jsp index.jsp course_query.jsp course_selection.jsp course_withdraw.jsp selection_query.jsp 项目代码 checkSelectionStatus.jsp % page contentType"text/html;charsetUTF-8" language"java&q…

图像处理:均值滤波算法

目录 前言 概念介绍 基本原理 Opencv实现中值滤波 Python手写实现均值滤波 参考文章 前言 在此之前&#xff0c;我曾在此篇中推导过图像处理&#xff1a;推导五种滤波算法&#xff08;均值、中值、高斯、双边、引导&#xff09;。这在此基础上&#xff0c;我想更深入地研…

4月23号软件更新资讯合集.....

微软发布 Web 渲染引擎 Babylon.js 6.0 Babylon.js 是一个强大、简单、开放的游戏和 Web 渲染引擎&#xff0c;并被封装在一个友好的 JavaScript 框架中。 Babylon.js 6.0 带来了性能改进、渲染增强和一系列新功能。 新物理插件 Havok 团队通过一个特殊的新 WASM 插件和对 Ba…

sar基本命令格式操作及使用方法学习笔记

目录 SAR说明Centos7安装Sar命令sar命令显示时间改为24小时制查询某天某个时间段内的数据 SAR说明 sar是一个采集&#xff0c;报告和存储计算机负载信息的工具。自下载安装好后每10分钟对系统性能进行一次采集&#xff0c;每天的日志文件保存再/var/log/sa/下&#xff0c;sa17…

Docker 部署Redis

由于项目需要&#xff0c;上了redis。公司用的是OKD4.x&#xff0c;所以在自己的环境上也直接上docker&#xff0c;比下载、编译、安装省心多了。 1、下载镜像 官网地址&#xff1a;https://hub.docker.com/_/redis 我选择的是docker pull redis:7.0-bullseye 具体版本号的含…

Web3D包装生产线 HTML5+Threejs(webgl)开发

生产线三维可视化解决方案就是通过物联网、虚实联动和三维建模等先进技术&#xff0c;以一个3D立体模型展现出来&#xff0c;可以让我们很直观的看到生产线的运作以及对数据的监控。3D运用数据孪生技术可以让工业3D物联网管理系统的界面变得非常的简单易看&#xff0c;并且能够…

软件测试的新技术和方法

作为一位资深的IT领域博主&#xff0c;我一直在关注软件测试领域的发展趋势。随着技术的不断发展&#xff0c;软件测试领域也在不断更新换代。在本文中&#xff0c;我将分享一些最新的软件测试技术和方法&#xff0c;希望能对广大软件测试工程师提供一些参考。 一、自动化测试…

【教程】保姆级红米AX6000刷UBoot和OpenWrt固件

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 目录 开启SSH 刷入UBoot 刷入Openwrt 设置Openwrt 刷回小米原厂固件 开启SSH 1、下载官方指定版本固件&#xff1a;https://share.qust.me/redmi-ax6000-1.2.8.bin 2、进入路由器后台升级固件&#xff1a;h…

Nginx +Tomcat 负载均衡,动静分离集群

介绍 通常情况下&#xff0c;一个 Tomcat 站点由于可能出现单点故障及无法应付过多客户复杂多样的请求等情况&#xff0c;不能单独应用于生产环境下&#xff0c;所以我们需要一套更可靠的解决方案 Nginx 是一款非常优秀的http服务软件&#xff0c;它能够支持高达 50000 个并发…

IndexedDB的包装器JsStore - 分页功能

JsStore是IndexedDB的包装器。它提供了简单的SQL像api&#xff0c;这是容易学习和使用。IndexedDb查询可以在web worker内部执行&#xff0c;JsStore通过提供一个单独的worker文件来保持这种功能。 由于之前使用IndexedDB时&#xff0c;提供api不太丰富&#xff0c;就自己写了一…

Ubuntu2204安装pycharm社区版

最近在学习人工智能相关的知识&#xff0c;比较流行的开源框架对windows操作系统的支持并不太友好&#xff0c;因此把学习测试的环境搭在了Ubuntu2204上。一开始我都是在win10系统中的pycharm上写python代码&#xff0c;然后再上传到Ubuntu2204中运行测试&#xff0c;这么做降低…

GIS基础概念与开发实践

GIS的应用价值 呈现、还原、规划空间信息数据挖掘、统计分析等等 Q&#xff1a;这么多软件可以做GIS&#xff0c;但是格式又不一样&#xff0c;怎么办&#xff1f;这普需要一个标准的出现。 GIS标准&#xff1a;OGC标准 不同GIS软件对空间数据定义和存储结构不同&#xff0…

远程访问及控制ssh

SSH远程管理 OpenSSH服务器 SSH(Secure Shell) 协议 是一种安全通道协议。主要用来实现字符界面的远程登录、远程复制等功能。对通信数据进行了加密处理&#xff0c;用于远程管理其中包括用户登录时输入的用户口令。因此SSH协议具有很好的安全性------------&#xff08;同样…

C嘎嘎~~ 【初识C++ 下篇】

初识C 下篇 1.引用1.1引用的概念1.2引用的特点1.3常引用1.4引用使用的场景1.5引用和指针的区别 2.指针空值 --- nullptr3.内联函数3.1 内联函数的概念3.2内联函数的使用场景3.3内联函数的特性 1.引用 1.1引用的概念 相信大家小时候&#xff0c; 肯定有小名、绰号、亲朋好友的昵…

查询提速 20 倍,Apache Doris 在 Moka BI SaaS 服务场景下的应用实践

导读&#xff1a; MOKA 主要有两大业务线 MOKA 招聘&#xff08;智能化招聘管理系统&#xff09;和 MOKA People&#xff08;智能化人力资源管理系统&#xff09;&#xff0c;MOKA BI 通过全方位数据统计和可灵活配置的实时报表&#xff0c;赋能于智能化招聘管理系统和人力资源…

scratch甲壳虫走迷宫 中国电子学会图形化编程 少儿编程 scratch编程等级考试一级真题和答案解析2023年3月

目录 scratch甲壳虫走迷宫 一、题目要求 1、准备工作 2、功能实现 二、案例分析

ESP32学习二-环境搭建(ESP-IDF V5.0,Ubuntu18.4)

一、准备事项 Ubuntu 18.04.5。具体安装可以参考如下链接。使用VMware安装Ubuntu虚拟机和VMware Tools_t_guest的博客-CSDN博客 乐鑫官方也提供了安装的相关操作。有兴趣可以参考。 快速入门 - ESP32 - — ESP-IDF 编程指南 v5.0.1 文档 注&#xff1a;提前说明&#xff0c;因…

Android权限描述

问题 我们常常在写apk的时候申请一些相关权限。想知道每个权限的作用&#xff0c;可以查询权限声明的地方。 1、三方页面&#xff1a; https://manifestdestiny.reveb.la/ 2、源码注释 /frameworks/base/core/res/AndroidManifest.xml <!-- SystemApi TestApi Allows a…

部署 Exsi 7.0.3

文章目录 1. 下载介质2. u盘引导安装启动盘3. 硬件连接4. 安装 EXSI 7.0.3 1. 下载介质 下载 VMware-VMvisor-Installer-7.0U3l-21424296.x86_64.iso 安装 EXSI 7.0.3 可参考: https://www.dinghui.org/vmware-iso-download.html 2. u盘引导安装启动盘 工具 https://www.v…

fitlog使用教程(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 fitlog包是用于自动版本管理和自动日志记录的Python包&#xff0c;是fastNLP团队开发的。 fitlog 中文文档 — fitlog 文档 他们团队的文档写的真的不行&#xff0c;崩溃&#xff0c;FastNLP也很难用&#xff0c;fitlog也很难用&#xff0c;中…