検証コードロジック
また、以前にプロジェクトで検証コードを作成しました。検証コードを生成する多くのコードがインターネットにあり、一部のサードパーティのJARパッケージも美しい検証コードを生成できます。検証コードロジックは非常に簡単です。これは、ログインページに画像タグを配置することで、SRCはコントローラーを指します。このコントローラーは、生成された画像を返し、出力ストリームをページに返します。画像を生成している間、画像上のテキストはセッションに配置されます。ログインするときは、入力された検証コードを持参して、セッションから取り出します。 2つを比較します。教師は、Spring Security統合検証コードを使用することは私が言ったことと同じであると言いましたが、より標準化され、一般的です。
Spring Securityは一連のフィルターチェーンであるため、ここでは検証コードもフィルターとして宣言され、フィルターチェーンのログインフィルターの前に追加され、例外クラスをカスタマイズして検証コードのエラーメッセージに応答します。
コード構造:
検証コードはコアプロジェクトに配置され、構成はブラウザプロジェクトで行われます。
メインコード:
1。ImageCode:
まず、ImageCodeクラスは検証コードの画像、テキスト、および有効期限をカプセル化します
パッケージcom.imooc.security.core.validate.code; Import java.awt.image.bufferedimage; import java.time.localdatetime; import java.localime;/***検証コード* classname:imagecode* @description:@author lihhayang* @descripation*/dated edate edcide edimecd BufferedImage画像;プライベート文字列コード; Private LocalDateTime Expiretime; //有効期限ポイント/** * * <p>説明:</p> * @param image * @param code * @param expiretn有効期限がいくつあるのか */public imagecode(bufferedimage image、string code、int expiretn){super(); this.image = image; this.code = code; //有効期間=現在の時間 +期限切れの秒数this.expiretime = localdateTime.now()。plusseconds(expiretn); } public ImageCode(bufferedImage Image、string code、localdateTime expiretime){super(); this.image = image; this.code = code; this.expiretime = expiretime; } / ***検証コードの有効期限が切れているかどうか* @description:検証コードが期限切れになったかどうか* @param @return trueの有効期限が切れていない、虚偽の期限が切れていない* @return boolean true true expired* @author lihaoyang* @date 3月2日、2018年3月2日* / public boolean isexpired() } public bufferedimage getImage(){return image; } public void setimage(bufferedimage image){this.image = image; } public string getCode(){return Code; } public void setCode(string code){this.code = code; } public localDateTime getExpireTime(){return expiretime; } public void setExpireTime(localDateTime expiretime){this.expiretime = expiretime; }}VerifyCode:検証コードを生成するためのツールクラス。ここでhttp://www.cnblogs.com/lihaoyang/p/7131512.html、もちろん、サードパーティの瓶パッケージを使用することもできますが、それは問題ではありません。
validAteCodeException :カプセル化された検証コードの例外
/** * @title:validatecodeexception.java * @package com.imooc.security.core.validate.code * @description:todo * @author lihaoyang * @date 3月2日org.springframework.security.core.authenticationexception;/*** classname:balitecodeexception* @description:@description:検証コードエラー例外、認証例外Springセキュリティ* @author lihaoyang* @date 3月2日SerialVersionUid = 1L; public validAteCodeException(String MSG){super(msg); }}validAteCodeFilter :検証コードフィルター
ロジック:resensiting onecreperRequestFilterは、フィルターが毎回1回のみ呼び出され(理由がわからない)、認証に故障したプロセッサに注入され、検証が失敗したときに呼び出されることを保証します。
パッケージcom.imooc.security.core.validate.code; Import java.io.ioexception; import javax.servlet.filterchain; Import javax.servlet.servletexception; Import javax.servlet.servlet.http.httpservletrequest; Import.havax.http.htptsposonse; apache.commons.lang.stringutils; Import org.springframework.security.web.authentication.authenticationfailure Handler; import org.springframework.social.connect.web.httpsessionsionsionsionsionsionsionsionsionsionstrategy; org.springframework.web.bind.servletrequestbindingexception; Import org.springframework.web.bind.servletrequestutils; import org.springframework.web.context.request.Request.Servletwebrequest; Import org.springframework.web.filfterログイン検証コードフィルター * className:validAtecodefilter * @description: * andperrequestfilter:springによって提供されるツールは、フィルターが毎回1回のみ呼び出されることを保証します * @author lihaoyang * @date 3月2日 */パブリッククラスのvalidAtecodefilterは、かつてrecreured exprivate exprive exprive exprived; //セッションツールクラスのプライベートセッションTrategy SessionsTrategy = new HTTPSESSIONSIONSIONSTRATEY()を取得します。 @Override Protected void dofilterinternal(httpservletrequest request、httpservletresponse応答、filterchain filterchain)は、servletexception、ioexception、ioexception {//ログイン要求の場合、if(stringutils.equals( "/authentication/form"、requestureturi( && stringutils.equalsignorecase(request.getMethod()、 "post")){try {validate(new servletwebrequest(request)); } catch(validAteCodeException e){//エラーハンドラーを呼び出し、最後に独自のauthenticationfailurehandler.onauthenticationfailure(request、response、e)を呼び出します。 return; //メソッドを終了し、フィルターチェーンを呼び出すことなく}} //ログイン要求ではなく、他のフィルターチェーンfilterchain.dofilter(リクエスト、応答)を呼び出します。 } /** *検証検証コード * @description:検証検証コード * @param @param request * @param @throws servletrequestbindingexception * @return void * @throws validatecodeexception * @author lihaoyang * @date 3月2日、2018年3月2日セッションのImageCodeオブジェクトImageCode ImageCodeInsession =(ImageCode)SessionsTrategy.GetAttribute(request、validAteCodecontroller.session_key); //リクエスト文字列の[ImageCodeInRequest = servletRequestutils.getStringParameter(request.getRequest()、 "ImageCode"); //(Stringutils.isblank(imageCodeInRequest)){new balidAteCodeExcection( "検証コードは空にすることはできない"); } if(imageCodeInsession == null){新しいvalidAteCodeException( "検証コードが存在しない、検証コードを更新してください"); } if(imageCodeInsession.isexpired()){//セッションSessionsTrategy.RemoveAttribute(request、validateCodecontroller.session_key)から有効期限のある検証コードを削除します。新しいvalidAteCodeException(「検証コードが期限切れになった」を投げます。検証コードを更新してください」); } if(!stringutils.equalsignorecase(mageCodeInsession.getCode()、ImageCodeInRequest)){throw new balialateCodeException( "verification code error"); } //検証が渡され、セッションSessionsTrategy.RemoveAttribute(request、validAteCodecontroller.session_key)で確認コードを削除します。 } public AuthenticationFailureHandler getAuthenticationFailureHandler(){return AuthenticationFailureHandler; } public void setAuthenticationFailureHandler(AuthenticationFailureHandler AuthenticationFailureHandler){this.authenticationfailurehandler = authenticationFailureHandler; }}validAteCodeController :検証コード制御を生成します
パッケージcom.imooc.security.core.validate.code; Import java.io.ioexception; Import javax.imageio.imageio; Import javax.servlet.httpservletrequest; Import javax.servlet.httpsertresponse; Import; org.springframework.social.connect.web.httpsessionsessionsionsionsionsionsionsionsionsionsionsionsionsionsionsionsionsionsionsionsionsionsionsectial.social.connect.web.sessionstrategy; Import org.springframework.web.bind.annotation.getMapping; Import org.springframework.web.bind.not.not.not.not.not.not. Web.Context.Request.ServletWebRequest;/** *検証CodeControl * className:validAteCodecontroller * @description:dodo * @author lihhooyang * @date March 1、2018 */ @RestControllerPublic sittecontroller "session_key_image_code"; //セッションを取得するプライベートセッションTrategy SessionsTrategy = new httpsessionsessionstrategy(); @getMapping( "/verifyCode/image")public void createCode(httpservletrequest request、httpservletResponse応答)IoException {imageCode imageCode = createImageCode(request、response); sessionstrategy.setattribute(new servletwebrequest(request)、session_key、imagecode); Imageio.write(imageCode.getImage()、 "jpeg"、respons.getOutputStream()); } private imageCode createImageCode(httpservletrequest request、httpservletResponse応答){verifyCode verifyCode = new verifyCode(); new ImageCode(verifyCode.getImage()、verifyCode.getText()、60)を返します。 }}browsersecurityconfigのフィルター構成:
パッケージcom.imooc.security.browser; import org.springframework.beans.factory.annotation.autowired; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; Import; springframework.security.config.annotation.web.configuration.websecurityconfigurerAdapter; Import org.security.crypt.bcrypt.bcryptpasswordencoder; Import org.springframework.security.crypto.password.password.password springframework.security.web.authentication.authenticationfailurehandler; Import org.springframework.security.web.authentication.authentication.failurehandler; import org.springframework.security.web.authentication.usernamepassowwordauthentication; com.imooc.security.core.properties.securityproperties; Import com.imooc.security.core.validate.code.validatecodefilter; @configuration // SecurityProperties; //カスタマイズされたプロセッサ@autowired private hunitionsuccesshandler imoocauthenticationsucceshandlerに成功した後; //カスタム認証失敗プロセッサ@autowired private authenticationfailurehandler imoocauthenticationfailurehandler; //それはorg.springframework.security.crypto.password.passwordencoder @bean publicsedencoder passwordcoder(){// bcryptpasswordencoderを実装していることに注意してください。 } //バージョン2:構成可能ログインページ@OverrideプロテクションVoid Configure(httpsecurity http)スロー例外{// validatecodefilter validatecodefilter validatecodefilter = new validatecodefilter(); //検証コードフィルターで独自のエラー処理を使用して、validAtecodeFilter.setAuthenticationFailureHandler(imoocauthenticationFailureHandler)に使用します。 //フォームログイン、セキュリティ=認証 +承認//http.httpbasic()をジャンプするために認証が必要なインターフェイスを実装してください。 authentication.loginpage( "/authentication/require")//ユーザー認証BrowserseCurityController //ログインフィルターusernamepasswordauthenticationfilterデフォルトのログインURLは「/ログイン」です。 postprocessor.failurehandler(imoocothenticationfailurehandler)//ログイン障害後の処理。 SecurityProperties.getBrowser()。getLoginPage()、//ログインページを保存しますが、フィルターではなく、エラー「/verifyCode/image ")。primitall()//検証code.anyRequest()// request.authenticated()//アイデンティティ認証は必須です。 }}ログインページ:ログインページは比較的大まかに作成されています。実際、検証コードの入力がフォーカスを失ったときに、検証コードを確認できます。クリック画像を使用して検証コード関数を更新することもできます。そのため、ここでは行いません。
<body>デモログインページ。 <br> <form action = "/authentication/form" method = "post"> <table> <tr> <td> username:</td> <td> <入力タイプ= "text" name "/> </td> <td> </td> </tr> <tr> <td>パスワード:</td> <入力="パスワード "パスワード=" <td> </td> </tr> <tr> <td>検証コード:</td> <td> <input type = "text" name "name =" imagecode "/> </td> <td> <img src ="/verifycode/image "/> </td> </tr <td" colspan = "light"> type = "submit"> login </button> </td> </tr> </table> </form> </body>
http:// localhost:8080/demo-login.htmlをご覧ください:
カスタム例外情報に応答します
一般的な機能は問題ありません。ただし、検証コード画像の幅と高さ、有効期限、フィルター処理されたURL、および検証コードロジックなど、すべてが記述されているように、それは十分に普遍的ではありません。これらは生き生きとすることができ、今では検証コードをフィルターにすることの利点が反映されています。フィルタリングする必要があるURLを構成できます。ログインページで検証コードを必要とするだけでなく、より一般的なものである場合があります。
1.一般化修正のための検証コードの基本パラメーターは一致させることができます
構成可能になり、アプリケーションはモジュールを指し、自分で構成します。構成されていない場合は、デフォルトの構成を使用します。さらに、構成は、リクエストURLまたはアプリケーションで宣言できます。教師は確かに教師であり、コードは非常に用途が広いです!
私が達成したい効果は、Application.Propertiesでこのような構成を作成することです。
#verification code image width、height、nome ob ob ob ob ob ob yimooc.security.code.image.width = 100imooc.security.code.image.height = 30imooc.security.code.image.length = 6
次に、検証コードの効果を制御できます。検証コードは画像検証コードとSMS検証コードにも分割されているため、Springbootのカスタム構成ファイルを使用し、対応するJavaクラスを宣言する必要があるレベル.code.imageを使用できます。
Code属性は、SecurityPropertiesで宣言する必要があります。
パッケージcom.imooc.security.core.properties; import org.springframework.boot.context.properties.configurationproperties; Import org.springframework.context.annotation.configuration;/*** Consifuration* ClassPorties* @describution* custimproperties* custipuration* custipuration* custipuration* custipuration* custipuration* custipuration* custipuration imooc.security in application.properties * * imooc.security.browser.loginpage = /demo-login.html *ブラウザー構成はブラウザープロパティに読み取られます *これはポイントで分割され、レベルとレベルはクラス * @author lihoyang * @date february 28の属性に対応します。 2018*/@configurationProperties(prefix = "imooc.security")public class securityproperties {private browserproperties browser = new browserproperties(); private validAteCodeProperties code = new validAteCodeProperties(); public browserproperties getBrowser(){return browser; } public void setBrowser(browserproperties browser){this.browser = browser; } public validAteCodeProperties getCode(){return Code; } public void setCode(validAteCodePropertiesコード){this.code = code; }}validAteCodeProperties:
パッケージcom.imooc.security.core.properties;/***検証コード構成* classname:validatecodeproperties* @description:検証コード構成、検証コードには画像検証コード、SMS検証コードなどが含まれます。 ImageCodeProperties Image = new ImageCodeProperties(); public ImageCodeProperties getImage(){return image; } public void setimage(imageCodeProperties Image){this.image = image; }}ImageCodeProperties:
パッケージcom.imooc.security.core.properties;/***画像検証コード構成クラス* classname:imagecodeproperties* @description:画像検証コード構成クラス* @author lihaoyang* @date 2018*/public class imageCodeProperties {//写真の幅int width = 67; //画像高さプライベートINT高さ= 23; //検証コード文字の数プライベートint length = 4; //有効期限はprivate int expirein = 60; public int getWidth(){return width; } public void setWidth(int width){this.width = width; } public int getheight(){return height; } public void setheight(int height){this.height = height; } public int getLength(){return length; } public void setLength(int length){this.length = length; } public int getExpirein(){return expirein; } public void setExpirein(int expirein){this.expirein = expirein; }}リクエストレベルの構成の場合、リクエストに検証コードパラメーターが含まれている場合、リクエストを使用します。
validAteCodeControllerのcreateimageCodeメソッドを使用して、リクエストパラメーターにこれらのパラメーターがあるかどうかを判断するために制御します。その場合、検証コード生成クラスVerifyCodeに渡されます。これは、生成中に動的に制御できます。
private ImageCode CreateImageCode(httpservletrequestリクエスト、httpservletResponse応答){//最初にリクエストの文字の長さ、幅、および数値を読み取ります。 int height = servletRequestutils.getIntParameter(request、 "height"、securityproperties.getCode()。getImage()。getheight()); int charlength = this.securityproperties.getCode()。getImage()。getLength(); VerifyCode verifyCode = new verifyCode(width、height、charlength); new ImageCode(verifyCode.getImage()、verifyCode.getText()、this.securityProperties.getCode()。getImage()。getExpirein()); }VerifyCode:
public verifycode(int w、int h、int charlength){super(); this.w = w; this.h = h; this.charlength = charlength; }実験:デモプロジェクトでアプリケーションレベルの構成を実行します
フォームにログインして、リクエストレベルの構成を作成します
<img src = "/verifyCode/image?width = 200"/>
アクセス:
長さは、リクエストステージバンドのパラメーター200であり、高さは30、文字は6つの構成です。
2.普遍的な修正の検証コード傍受のためのインターフェイスを構成できます
最初の効果は、 application.propertiesで傍受する必要があるインターフェイスを動的に構成することです。
ImageCodeProperties 、新しい属性を追加しました: private String url 。 //インターセプトされたURLは、上記の画像の構成と一致することです。
コア、検証コードフィルターを変更する必要があります。
1.インターセプターに設定されたセットを宣言して、インターセプトする必要がある構成ファイルに構成されたURLを保存します。
2。初期化ビーンインターフェイスを実装します。目的:他のすべてのパラメーターが組み立てられたら、インターセプトする必要があるURLの値を初期化し、AfterPropertiessetメソッドを書き換えて実装します。
3. SecurityPropertiesを挿入し、構成ファイルを読み取ります
4.マッチャーであるAntpathmatcherツールクラスをインスタンス化します
5. BrowsersecurityConfigをブラウザプロジェクトで設定して、AfterPropertiessetメソッドを呼び出します。
6.アプリケーションでフィルタリングするURLを構成します。モジュールを参照するデモプロジェクトのプロパティ。
validAteCodeFilter:
/** *ログイン検証コードフィルターを処理 * className:validAtecodeFilter * @description: * wundperrequestfilter:springを継承するために、spring intilemingbeanインターフェイスの目的を実装することを確認します。 2018*/public class validAteCodeFilterがextendを拡張します。 //セッションツールクラスのプライベートセッションTrategy SessionsTrategy = new HTTPSESSIONSIONSIONSTRATEY()を取得します。 //プライベートセット<string> urls = new Hashset <>(); // Configuration Private SecurityProperties SecurityPropertiesを読み取ります。 //スプリングツールクラスプライベートantpathmatcher antpathmatcher = new antpathmatcher(); @Override public void avherpropertiesset()throws servletexception {super.afterpropertiesset(); //構成されたインターセプトされた文字列[] configurls = stringutils.splitbyWholeSeparatorPreserveallTokens(securityProperties.getCode()。getImage()。getUrl()、getUrl()、 "、"); for(string configurl:configurls){urls.add(configurl); } //ログインリクエストは、urls.add( "/authentication/form")を傍受する必要があります。 } @Override Protected void dofilterinternal(httpservletrequest request、httpservletresponse応答、filterchain filterchain)Servletexception、ioexception、ioexception、ioException { / ***構成可能な検証検証*要求されたURLと構成されたURLが一致するかどうかを判断します。 for(string url:urls){if(antpathmatcher.match(url、request.getRequesturi())){action = true; }} if(action){try {validate(new servletwebrequest(request)); } catch(validAteCodeException e){//エラーハンドラーを呼び出し、最後に独自のauthenticationfailurehandler.onauthenticationfailure(request、response、e)を呼び出します。 return; //メソッドを終了し、フィルターチェーンを呼び出すことなく}} //ログイン要求ではなく、他のフィルターチェーンfilterchain.dofilter(request、response)を呼び出します。 } //無関係なコードを省略します、}browsersecurityconfig:
URLを構成します:
#verificationコードインターセプトインターフェイス構成imooc.security.code.image.url =/user、/user/*
テスト: /user /user /1が傍受されました
検証コードを記述せずにログインページにアクセスしてください。
期待と一致しています。この時点で、インターセプトインターフェイスの動的構成が完了します
3.検証コード生成ロジックは構成可能です
よりよく書かれたプログラムには一般に、オープンなインターフェイスがあり、ユーザーが実装をカスタマイズできるようにします。実装していない場合は、デフォルトの実装を使用します。これを行い、検証コードの生成を自分で実装できるようにしましょう。検証コード生成ロジックを構成可能にしたい場合は、画像検証コードジェネレーターのクラスを作成することはできません。検証コードの生成を抽出して、検証コードジェネレーター()を生成する方法であるInterface validAteCodeGeneratorに抽出する必要があります。検証コードには画像検証コード、SMS検証コードなどもあるため、画像検証コード実装ImageCodeGeneratorなど、検証モジュールでデフォルトの実装を作成します。 ImageCodeGeneratorでは、このクラスに@Componentアノテーションを追加しません。次に、検証コードBeanを書き込むConfiguration class balidatecodebeanconfigを使用します。この構成クラスは、画像検証コードの実装ImageCodeGenerator、SMS検証コードなど、さまざまな必要な検証コード実装豆を構成します。それらの戻りタイプはすべて有効なAteCodeGeneratorです。 @ConditionAlonMissingBean(name = "ImageCodeGenerator")注釈を使用します。現在のスプリングコンテナにImageCodeGeneratorという名前のBeanが使用され、構成がない場合、他の人がモジュールを参照する場合、他の人が検証コードを実装して、実装クラスの名前を生成して、独自の実装を使用します。
メインコード:
コードジェネレーターインターフェイスvalidAtecodeGenerator:
パッケージcom.imooc.security.core.validate.code; Import org.springframework.web.context.request.servletwebrequest;/** *検証コード生成インターフェイス * classname:validatecodegenerator * @description:dodo * @author lihayang * @date 2、2018 */public interped / ** *画像検証コード生成インターフェイス * @description:todo * @param @param request * @param @return * @return imagecode * @throws * @author lihaoyang * @date 2018年3月2日 * / imagecodeジェネレーター(servletwebrequestリクエスト);}
画像検証コードジェネレーターがImageCodeGeneratorを実装します。
パッケージcom.imooc.security.core.validate.code; Import org.springframework.beans.factory.annotation.autowired; Import org.springframework.stereotywe.component; Import org.springframework.web.web.bind.servletreatutils; Import; org.springframework.web.context.request.servletwebrequest; Import com.imooc.security.core.properties.securityproperties;/** *画像検証コードジェネレーションクラス * className:imagecodeGenerator * @description:@author lihhoyang * @date 2 date 2 date 2 date 2 date 2 date clas validAteCodeGenerator {@Autowired Private SecurityProperties SecurityProperties; @Override public ImageCodeジェネレーター(servletwebrequest request){//リクエストから最初に読み取り、長さ、幅、文字パラメーターがあるかどうか。ある場合、それを使用し、デフォルトのint width = servletRequestutils.getIntParameter(request.getRequest()、 "width"、securityproperties.getCode()。getWidth()); int height = servletRequestutils.getIntParameter(request.getRequest()、 "height"、securityproperties.getCode()。getImage()。getheight()); int charlength = this.securityproperties.getCode()。getImage()。getLength(); VerifyCode verifyCode = new verifyCode(width、height、charlength); new ImageCode(verifyCode.getImage()、verifyCode.getText()、this.securityProperties.getCode()。getImage()。getExpirein()); } public SecurityProperties GetSecurityProperties(){return SecurityProperties; } public void setSecurityProperties(SecurityProperties SecurityProperties){this.securityProperties = securityProperties; }}validatecodebeanconfig:
パッケージcom.imooc.security.core.validate.code; Import org.springframework.beans.factory.annotation.autowired; Import org.springframework.boot.autoconfigure.condition.condition.condition.condition.condition.condition.onmissingbean; org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; Import com.imooc.security.core.properties.securityproperties;画像検証コードの実装やSMS検証コードの実装など、validAtecodegenerator *の実際の実装クラスのBeanを構成 * @author lihaoyang * @date 2018 */ @configurationpublic class validatecodebeanconfig {@autowired securitysporties secuityproperties; /** * @description: * @ConditionAlonMissingBeanアノテーションとは、スプリングコンテナにImageCodeGeneratorがない場合、機能がプログラムをより拡張可能にすることを意味します。構成クラスはコアモジュールで構成されています。これは、モジュール *を指すプロジェクトに独自の実装がある場合、それがvalimAtecodegeneratorインターフェイスを実装し、独自の実装を定義することを意味します。名前はImageCodeGeneratorとも呼ばれ、 *はアプリケーションレベルの実装を実装するために使用され、そのような実装がない場合は、このデフォルトの実装を使用します。 * @param @return * @return validAteCodeGenerator * @throws * @Author lihaOyang * @date 2018年3月5日 */ @bean @conditionalonmissingbean(name = "imagecodegenerator")public validatecodegenerator emagycodegenerator(){imagecodegenerator = Generator(); codegenerator.setsecurityproperties(securityproperties); return codeNerator; }}このようにして、モジュールがこの検証コードモジュールを参照する場合、次のような実装をカスタマイズします。
パッケージcom.imooc.code; Import org.springframework.stereotype.component; Import org.springframework.web.context.request.servletwebrequest; Import com.imooc.security.core.validate.code.imagecode;インポートcom.imooc.security.core.validate.code.validatecodegenerator; @component( "imagecodegenerator")public class demoimagecodegeneratorは、valimatecodegenerator {@Override public imagecodeジェネレーター(servletwebrequest request){servletwebrequest request){system.println( "ener.println(" ener.println( "ener.println)){servletwebrequest request) nullを返します。 }}このようにして、valimatecodebeanconfigが検証コードBeanを構成すると、ユーザーのカスタム実装が使用されます。
完全なコードはgithubに配置されています:https://github.com/lhy1234/spring-security
要約します
上記は、編集者が紹介したSpring Security Image Verification Code Code関数の例です。それがあなたに役立つことを願っています。ご質問がある場合は、メッセージを残してください。編集者は時間内に返信します。 wulin.comのウェブサイトへのご支援ありがとうございます!