IoC Starterを使用したWebアプリ。 IOCコンテキスト、IOC Web、およびIOC ORMを使用した基本的なクエリマッピング

画像

はじめに



最初のリリースから多くの時間が経過しました( 前の記事へのリンク )。 何が変わった?









**モジュール









フレームワーク構造

構造







「もちろん、これはすべて良いことです」とあなたは言いますが、実際にはすべてうまくいきますか?

「はい、機能します。カットをお願いします。」







実装プロセスの例



この例を実装するには、Maven 3とIntelijj Idea 2018.2を使用します。







1)依存関係を接続します。







<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>example-webapp</artifactId> <groupId>org.ioc</groupId> <packaging>jar</packaging> <version>0.0.1</version> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <.source>1.8</source> <target>1.8</target> </configuration> <executions> <execution> <id>default-testCompile</id> <phase>test-compile</phase> <goals> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.ioc</groupId> <artifactId>context-factory</artifactId> <version>2.2.4.STABLE</version> </dependency> <dependency> <groupId>org.ioc</groupId> <artifactId>orm-factory</artifactId> <version>2.2.4.STABLE</version> </dependency> <dependency> <groupId>org.ioc</groupId> <artifactId>web-factory</artifactId> <version>2.2.4.STABLE</version> </dependency> </dependencies> <repositories> <repository> <id>context</id> <url>https://raw.github.com/GenCloud/ioc_container/context</url> </repository> <repository> <id>cache</id> <url>https://raw.github.com/GenCloud/ioc_container/cache</url> </repository> <repository> <id>threading</id> <url>https://raw.github.com/GenCloud/ioc_container/threading</url> </repository> <repository> <id>orm</id> <url>https://raw.github.com/GenCloud/ioc_container/orm</url> </repository> <repository> <id>web</id> <url>https://raw.github.com/GenCloud/ioc_container/web</url> </repository> </repositories> </project>
      
      





**彼はまだMaven Centralに移動していません。







プロジェクト構造:

構造

標準のMVCパターンではありませんか?







アプリケーションへのエントリポイントを作成します。







 package org.examples.webapp; import org.ioc.annotations.context.ScanPackage; import org.ioc.annotations.modules.CacheModule; import org.ioc.annotations.modules.DatabaseModule; import org.ioc.annotations.modules.ThreadingModule; import org.ioc.annotations.modules.WebModule; import org.ioc.context.starter.IoCStarter; @WebModule @CacheModule @ThreadingModule @DatabaseModule @ScanPackage(packages = {"org.examples.webapp"}) public class AppMain { public static void main(String[] args) { IoCStarter.start(AppMain.class); } }
      
      





**説明:

アノテーション@ScanPackages-コンポーネントを識別するためのコンテキストパッケージを定義します(一般の人々-「ビン」)。

アノテーション@WebModule -Webファクトリの接続と初期化に使用されます。

アノテーション@CacheModule-キャッシュファクトリの接続と初期化に使用され、ORMが正常に機能するために使用されます(将来のバージョンでは、アノテーションは不要になります)。

アノテーション@ThreadingModule-スレッドファクトリの接続と初期化に使用され、Webファクトリの正しい操作に使用されます(将来のバージョンでは、アノテーションは不要になります)。

アノテーション@DatabaseModule -ORMファクトリの接続と初期化に使用されます。

すべてのファクトリーにはデフォルトのコンフィギュレーターがあり、ファクトリーが使用する設定の機能を再定義することで独自に変更できます(各モジュールアノテーションでクラスコンフィギュレーターが再定義されます- クラス<?> AutoConfigurationClass()default WebAutoConfiguration.class )、または@Excludeアノテーションを使用して構成を無効にします教室。

IoCStarterユーティリティは、メインのコンテキスト初期化クラスです。







まあ、すべてが準備ができているようで、コンテキストは初期化され、ウェブはデフォルトのポート8081で動作しますが、リンクはありません。サイトにアクセスしても、何も提供されません。 さあ、先に進みましょう。







モジュールの構成ファイルを作成しましょう。 デフォルトでは、すべての設定は{working_dir} /configs/default_settings.propertiesからロードされます-適切なパスに沿って作成します。







 # Threading ioc.threads.poolName=shared ioc.threads.availableProcessors=4 ioc.threads.threadTimeout=0 ioc.threads.threadAllowCoreTimeOut=true ioc.threads.threadPoolPriority=NORMAL # Event dispather # -  ()    ( ) ioc.dispatcher.availableDescriptors=4 # Cache #   (EhFactory|GuavaFactory) cache.factory=org.ioc.cache.impl.EhFactory # Datasource #   (-, -  ) #LOCAL, LOCAL_SERVER, REMOTE datasource.orient.database-type=LOCAL #   datasource.orient.url=./database #    (   ) datasource.orient.database=orient #   datasource.orient.username=admin #   datasource.orient.password=admin #       (create, dropCreate, refresh, none) datasource.orient.ddl-auto=dropCreate #   ,      datasource.orient.showSql=true # Web server #     web.server.port=8081 #   SSL  web.server.ssl-enabled=false # in seconds #   ( 7200 . = 2 ) web.server.security.session.timeout=300 #  - web.server.velocity.input.encoding=UTF-8 web.server.velocity.output.encoding=UTF-8 #  - web.server.velocity.resource.loader=file #   web.server.velocity.resource.loader.class=org.apache.velocity.runtime.resource.loader.FileResourceLoader #    - web.server.velocity.resource.loading.path=./public
      
      





次に、ユーザーエンティティとその管理リポジトリが必要です。

TblAccountエンティティの実装:







 package org.examples.webapp.domain.entity; import org.ioc.web.security.user.UserDetails; import javax.persistence.*; import java.util.Collections; import java.util.List; @Entity public class TblAccount implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private long id; @Column(name = "username") private String username; @Column(name = "password") private String password; @Transient private String repeatedPassword; public String getRepeatedPassword() { return repeatedPassword; } public void setRepeatedPassword(String repeatedPassword) { this.repeatedPassword = repeatedPassword; } @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public List<String> getRoles() { return Collections.singletonList("ROLE_USER"); } }
      
      





**説明:

JPAをサポートするすべてのフレームワークと同様に、すべてが標準です。 @ .Entityを使用してエンティティをマップし、 @ .Idアノテーションを使用して主キーを作成し、 @ Columnアノテーションを使用して列をマップします UserDetailsから継承して、 セキュリティモジュール内のエンティティを識別します。







TblAccountRepositoryエンティティリポジトリの実装:







 package org.examples.webapp.domain.repository; import org.examples.webapp.domain.entity.TblAccount; import org.ioc.annotations.context.IoCRepository; import org.ioc.orm.repositories.CrudRepository; import javax.transaction.Transactional; @IoCRepository public interface TblAccountRepository extends CrudRepository<TblAccount, Long> { @Transactional TblAccount findByUsernameEq(String username); }
      
      





**説明:

アノテーション@IoCRepository-コンテキストによってクラスがリポジトリであり、 「異なる」処理が必要であることを決定します。

標準のCRUD機能をサポートします。









上記の実装( TblAccount findByUsernameEq(String username) )のようにキーワードで関数をオーバーライドし、登録済みクエリ( NamedQuery )を呼び出すことにより、クエリの自動生成をサポートします。







findByUsernameEq(文字列ユーザー名)関数- ユーザー名フィールドでエンティティを検索します。 生成されたリクエスト:







  select * from tbl_account where username = 'username'
      
      





次に、ビジネスロジックを管理するためのレベルが必要です。

AccountServiceの実装:







 package org.examples.webapp.service; import org.examples.webapp.domain.entity.TblAccount; import org.examples.webapp.domain.repository.TblAccountRepository; import org.examples.webapp.responces.IMessage; import org.ioc.annotations.context.IoCComponent; import org.ioc.annotations.context.IoCDependency; import org.ioc.web.model.http.Request; import org.ioc.web.security.configuration.SecurityConfigureAdapter; import org.ioc.web.security.encoder.bcrypt.BCryptEncoder; import org.ioc.web.security.user.UserDetails; import org.ioc.web.security.user.UserDetailsProcessor; import java.util.Objects; import static org.examples.webapp.responces.IMessage.Type.ERROR; import static org.examples.webapp.responces.IMessage.Type.OK; @IoCComponent public class AccountService implements UserDetailsProcessor { @IoCDependency private TblAccountRepository tblAccountRepository; @IoCDependency private BCryptEncoder bCryptEncoder; @IoCDependency private SecurityConfigureAdapter securityConfigureAdapter; @Override public UserDetails loadUserByUsername(String username) { return tblAccountRepository.findByUsernameEq(username); } public void save(TblAccount tblAccount) { tblAccountRepository.save(tblAccount); } public void delete(TblAccount tblAccount) { tblAccountRepository.delete(tblAccount); } public IMessage tryCreateUser(String username, String password, String repeatedPassword) { if (username == null || username.isEmpty() || password == null || password.isEmpty() || repeatedPassword == null || repeatedPassword.isEmpty()) { return new IMessage(ERROR, "Invalid request parameters!"); } if (!Objects.equals(password, repeatedPassword)) { return new IMessage(ERROR, "Repeated password doesn't match!"); } final UserDetails userDetails = loadUserByUsername(username); if (userDetails != null) { return new IMessage(ERROR, "Account already exists!"); } final TblAccount account = new TblAccount(); account.setUsername(username); account.setPassword(bCryptEncoder.encode(password)); save(account); return new IMessage(OK, "Successfully created!"); } public IMessage tryAuthenticateUser(Request request, String username, String password) { if (username == null || username.isEmpty() || password == null || password.isEmpty()) { return new IMessage(ERROR, "Invalid request parameters!"); } final UserDetails userDetails = loadUserByUsername(username); if (userDetails == null) { return new IMessage(ERROR, "Account not found!"); } if (!bCryptEncoder.match(password, userDetails.getPassword())) { return new IMessage(ERROR, "Password does not match!"); } securityConfigureAdapter.getContext().authenticate(request, userDetails); return new IMessage(OK, "Successfully authenticated"); } public IMessage logout(Request request) { if (securityConfigureAdapter.getContext().removeAuthInformation(request)) { return new IMessage(OK, "/"); } return new IMessage(ERROR, "Credentials not found or not authenticated!"); } }
      
      





**説明:

アノテーション@IoCComponent-クラスをコンポーネントとして初期化するのに役立ちます。

アノテーション@IoCDependency-依存関係をクラスインスタンスに注入します。

BCryptEncoderユーティリティは、パスワード暗号化用のBCryptコーデックの実装です(これまでのところ唯一のコーデック)。

システムインスタンスSecurityConfigureAdapter-マッピングリクエストとユーザーセッションの操作に使用されます。

関数UserDetails loadUserByUsername-継承された関数UserDetailsProcessor 。ユーザーをセッションにロードし、認証フラグを設定するために使用されます(将来のSecurityでの認可の標準マッピングのため)

IMessage関数tryCreateUserは、ユーザー作成関数です。

IMessage関数tryAuthenticateUser-ユーザー認証関数。

関数IMessageログアウト -許可されたユーザーからセッションを消去する関数。

IMessageクラスは、ブラウザーで必要な情報(json応答)を表示するためのユーティリティクラスです。







 package org.examples.webapp.responces; public class IMessage { private final String message; private final Type type; public IMessage(String message) { this.message = message; type = Type.OK; } public IMessage(Type type, String message) { this.message = message; this.type = type; } public String getMessage() { return message; } public Type getType() { return type; } public enum Type { OK, ERROR } }
      
      





次に、リンク自体の実装が必要になります(リクエストマッピング):







 package org.examples.webapp.mapping; import org.examples.webapp.domain.entity.TblAccount; import org.examples.webapp.responces.IMessage; import org.examples.webapp.service.AccountService; import org.ioc.annotations.context.IoCDependency; import org.ioc.annotations.web.IoCController; import org.ioc.web.annotations.Credentials; import org.ioc.web.annotations.MappingMethod; import org.ioc.web.annotations.RequestParam; import org.ioc.web.annotations.UrlMapping; import org.ioc.web.model.ModelAndView; import org.ioc.web.model.http.Request; @IoCController @UrlMapping("/") public class MainMapping { @IoCDependency private AccountService accountService; @UrlMapping public ModelAndView index() { final ModelAndView modelAndView = new ModelAndView(); modelAndView.setView("index"); return modelAndView; } @UrlMapping(value = "signup", method = MappingMethod.POST) public IMessage createUser(@RequestParam("username") String username, @RequestParam("password") String password, @RequestParam("repeatedPassword") String repeatedPassword) { return accountService.tryCreateUser(username, password, repeatedPassword); } @UrlMapping(value = "signin", method = MappingMethod.POST) public IMessage auth(Request request, @RequestParam("username") String username, @RequestParam("password") String password) { return accountService.tryAuthenticateUser(request, username, password); } @UrlMapping("signout") public IMessage signout(Request request) { return accountService.logout(request); } @UrlMapping("loginPage") public ModelAndView authenticated(@Credentials TblAccount account) { final ModelAndView modelAndView = new ModelAndView(); modelAndView.setView("auth"); modelAndView.addAttribute("account", account); return modelAndView; } }
      
      





**説明:

アノテーション@IoCController-コンテキスト内のクラスをコントローラー(ブラウザー要求マッパー)として識別するのに役立ちます

アノテーション@UrlMapping-チャネルハンドラーによって処理された要求の存在について関数/クラスを分析する必要があることを示します。

パラメータ:









アノテーション@RequestParam-リクエストから受け取ったパラメーターの名前を決定するのに役立ちます。 デフォルトのリフレクション手段でメソッドパラメータの現在の名前を取得することは不可能であるため、余計なjavaassist依存関係をasmのシャーマンに接続するのは面倒でした。 したがって、リクエストから取得したこのパラメータ値を埋め込むためのパラメータの名前を決定するためのそのような方法。 GETタイプに類似したものがあります- @PathVariable-同じ動作原理( POSTと互換性がありません)。

@Credentialsアノテーション -許可されたユーザーの現在のデータを挿入します。そうでない場合、許可されたユーザーの情報がセッションにない場合はnullになります。

システムクラスリクエスト -コカ、ヘッダー、およびユーザーチャネルを含む着信リクエストに関する現在の情報。これは、後でこの点に関して何らかのファンタジーを持つプッシュメッセージに送信できます。

ユーティリティクラスModelAndViewは、拡張子のないリソースの名前と、リソースに埋め込むための属性を持つページモデルです。







それはすべてのようですが、いいえ-ユーザーのリクエストのアクセス可能なマッピングを設定する必要があります。







 package org.examples.webapp.config; import org.ioc.annotations.configuration.Property; import org.ioc.annotations.configuration.PropertyFunction; import org.ioc.web.security.configuration.HttpContainer; import org.ioc.web.security.configuration.SecurityConfigureProcessor; import org.ioc.web.security.encoder.Encoder; import org.ioc.web.security.encoder.bcrypt.BCryptEncoder; import org.ioc.web.security.filter.CorsFilter; import org.ioc.web.security.filter.CsrfFilter; @Property public class SecurityConfig implements SecurityConfigureProcessor { @Override public void configure(HttpContainer httpContainer) { httpContainer. configureRequests(). anonymousRequests("/", "/signup", "/signin"). resourceRequests("/static/**"). authorizeRequests("/loginPage", "ROLE_USER"). authorizeRequests("/signout", "ROLE_USER"). and(). configureSession(). expiredPath("/"); } @PropertyFunction public CsrfFilter csrfFilter() { return new CsrfFilter(); } @PropertyFunction public CorsFilter corsFilter() { return new CorsFilter(); } @PropertyFunction public Encoder encoder() { return new BCryptEncoder(); } }
      
      





**説明:

@Property注釈 -これが構成ファイルであり、初期化する必要があることをコンテキストに伝えます。

アノテーション@PropertyFunction-この関数が何らかのタイプを返し、コンポーネント(ビン)として初期化する必要があることを構成アナライザーに通知します。

インターフェースSecurityConfigureProcessor-リクエストのマッピングを設定するために使用されるユーティリティ。

HttpContainerモデルクラスは、ユーザーが指定した要求のマッピングを格納するユーティリティです。

CsrfFilterクラスは、 無効なリクエストのフィルターです(CSRFメカニズムの実装)。

CorsFilterクラスは、クロスオリジンリソース共有フィルターです。







AnonymousRequests関数 -要求の無制限の配列を受け入れ、許可されたユーザーとロールチェックを必要としません(ROLE_ANONYMOUS)。

resourceRequests関数 -リクエストの無制限の配列を受け取ります。具体的には、リソースファイルが複雑な処理(css、js、イメージなど)を必要としないパスを決定するのに役立ちます。

関数authorizeRequests-リクエストの無制限の配列を受け取り、許可されたユーザーとそのユーザーに固有の特定の役割が必要です。

ExpiredPath関数 -時間内に期限切れになったセッションをクリアすると、ユーザーはこのマッピング(リダイレクトリンク)によってリダイレクトされます。







さて、ページ、スクリプト、およびサイトスタイルがありました(詳しく説明しません)。







ネタバレ見出し

index.vm-メインページ







 <html> <head> <meta charset="utf-8"/> <title>IoC Test</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/style.css"/> <link rel="stylesheet" href="/static/css/pnotify.custom.min.css"/> <link rel="stylesheet" href="/static/css/pnotify.css"/> <link rel="stylesheet" href="/static/css/pnotify.buttons.css"/> </head> <body> <div class="container"> <h1>IoC Starter Test</h1> <br> <h4>Create user</h4> <br> <form id="creation"> <label for="username">Username: </label> <input type="text" id="username" name="username" class="color-input-field"/> <label for="password">Password: </label> <input type="password" id="password" name="password" class="color-input-field"/> <label for="repeatedPassword">Repeate: </label> <input type="password" id="repeatedPassword" name="repeatedPassword" class="color-input-field"/> <button type="button" class="btn btn-success btn-create">Sing up!</button> </form> <h4>Authenticate</h4> <br> <form id="auth"> <label for="username">Username: </label> <input type="text" id="username" name="username" class="color-input-field"/> <label for="password">Password: </label> <input type="password" id="password" name="password" class="color-input-field"/> <button type="button" class="btn btn-danger btn-auth">Sing in!</button> </form> </div> <script type="text/javascript" src="/static/js/jquery.js"></script> <script type="text/javascript" src="/static/js/bootstrap.min.js"></script> <script type="text/javascript" src="/static/js/scripts.js"></script> <script type="text/javascript" src="/static/js/pnotify.js"></script> <script type="text/javascript" src="/static/js/pnotify.buttons.js"></script> </body> </html>
      
      





auth.vm-許可されたユーザーを表示します







 <html> <head> <meta charset="utf-8"/> <title>IoC Test</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/style.css"/> <link rel="stylesheet" href="/static/css/pnotify.custom.min.css"/> <link rel="stylesheet" href="/static/css/pnotify.css"/> <link rel="stylesheet" href="/static/css/pnotify.buttons.css"/> </head> <body> <div class="container"> <h1>Authorized page</h1> <br> <h4>Test auth data</h4> <div id="auth_data"> #if($!account) <h4>Hello @$!account.username, You successfully authenticated!</h4> <br> <button type="button" class="btn btn-success btn-logout">Logout!</button> #end </div> </div> <script type="text/javascript" src="/static/js/jquery.js"></script> <script type="text/javascript" src="/static/js/bootstrap.min.js"></script> <script type="text/javascript" src="/static/js/scripts.js"></script> <script type="text/javascript" src="/static/js/pnotify.js"></script> <script type="text/javascript" src="/static/js/pnotify.buttons.js"></script> </body> </html>
      
      





scripts.js-サーバーへの要求情報を送受信するためのコントローラー







 $(function () { $(".btn-create").click(function () { var cooki = cookie(); document.cookie = 'CSRF-TOKEN=' + cooki; $.ajax({ url: "/signup", data: $('#creation').serialize(), headers: {'X-CSRF-TOKEN': cooki}, crossDomain: true, xhrFields: { withCredentials: true }, type: "POST" }).done(function (data) { switch (data.type) { case 'OK': new PNotify({ title: 'Success', text: data.message, type: 'success', hide: false }); break; case 'ERROR': new PNotify({ title: 'Error', text: data.message, type: 'error', hide: false }); break; } }); }); $(".btn-auth").click(function () { var cooki = cookie(); document.cookie = 'CSRF-TOKEN=' + cooki; $.ajax({ url: "/signin", data: $('#auth').serialize(), headers: {'X-CSRF-TOKEN': cooki}, crossDomain: true, xhrFields: { withCredentials: true }, type: "POST" }).done(function (data) { switch (data.type) { case 'OK': new PNotify({ title: 'Success', text: data.message, type: 'success', hide: false }); setTimeout(function () { window.location = "/loginPage"; }, 5000); break; case 'ERROR': new PNotify({ title: 'Error', text: data.message, type: 'error', hide: false }); break; } }); }); $(".btn-logout").click(function () { $.ajax({ url: "/signout", crossDomain: true, xhrFields: { withCredentials: true }, type: "GET" }).done(function (data) { switch (data.type) { case 'OK': new PNotify({ title: 'Success', text: 'Logouting...', type: 'success', hide: false }); setTimeout(function () { window.location = data.message; }, 5000); break; case 'ERROR': new PNotify({ title: 'Error', text: data.message, type: 'error', hide: false }); break; } }); }); }); function cookie(a) { return a // if the placeholder was passed, return ? ( // a random number from 0 to 15 a ^ // unless b is 8, Math.random() // in which case * 16 // a random number from >> a / 4 // 8 to 11 ).toString(16) // in hexadecimal : ( // or otherwise a concatenated string: [1e7] + // 10000000 + -1e3 + // -1000 + -4e3 + // -4000 + -8e3 + // -80000000 + -1e11 // -100000000000, ).replace( // replacing /[018]/g, // zeroes, ones, and eights with cookie // random hex digits ) }
      
      





コンパイルし、すべてを実行します。

すべてが正しければ、ダウンロードの最後に同様のものが表示されます。







ログ

[21.10.18 22:29:51:990] INFO web.model.mapping.MappingContainer:マッピングされたメソッド[/]、method = [GET]、[public org.ioc.web.model.ModelAndView org.examples.webapp .mapping.MainMapping.index()]

[21.10.18 22:29:51:993] INFO web.model.mapping.MappingContainer:マッピングされたメソッド[/ signup]、method = [POST]、[public org.examples.webapp.responces.IMessage org.examplesへ。 webapp.mapping.MainMapping.createUser(java.lang.String、java.lang.String、java.lang.String)]

[21.10.18 22:29:51:993] INFO web.model.mapping.MappingContainer:マッピングされたメソッド[/ signin]、method = [POST]、[public org.examples.webapp.responces.IMessage org.examplesへ。 webapp.mapping.MainMapping.auth(org.ioc.web.model.http.Request、java.lang.String、java.lang.String)]

[21.10.18 22:29:51:993] INFO web.model.mapping.MappingContainer:マッピングされたメソッド[/ signout]、method = [GET]、[public org.examples.webapp.responces.IMessage org.examplesへ。 webapp.mapping.MainMapping.signout(org.ioc.web.model.http.Request)]

[21.10.18 22:29:51:995] INFO web.model.mapping.MappingContainer:マッピングされたメソッド[/ loginPage]、method = [GET]、[public org.ioc.web.model.ModelAndView org.examplesへ。 webapp.mapping.MainMapping.authenticated(org.examples.webapp.domain.entity.TblAccount)]

[21.10.18 22:29:51:997] INFO ioc.web.factory.HttpInitializerFactory:ポート(s)で開始されたHttpサーバー:8081(http)







結果:

1)ホームページ

インデンク

2)登録

サンアップ

3)認証

auth

4)認証結果のページ(ログインとパスワードを正しく入力した後のリダイレクト)

結果

5)セッションから認証情報を消去し、ユーザーをスタートページにリダイレクトする

画像

6)権限のないユーザーによるセッション認証情報のページへのアクセスの試み

画像







終わり



このプロジェクトは開発中であり、「貢献者」と独創的なアイデアを歓迎します。このプロジェクトを単独で行うのは難しいからです。

プロジェクトリポジトリ

コンテキスト

ORMファクトリー

ウェブファクトリー



記事の現在の例

リポジトリには、 「examples」モジュールのすべての機能を使用した例もあります。「幸運をお祈りします」と言っているように、ご清聴ありがとうございました。








All Articles