この記事にはアイデアのみが含まれており、具体的かつ完全な実装を提供しません(ブロガーはそれを整理するにはあまりにも怠惰です)。ご質問がある場合や知りたい場合は、プライベートメッセージやコメントを送信できます。
背景
従来のJava Webの小規模および中規模プロジェクトでは、一般に、ロガーのID情報などのセッション情報を一時的に保存するためにセッションが使用されます。このメカニズムは、HTTPのCookieメカニズムを借用することで実装されていますが、アプリがリクエストするたびにCookie情報を保存および共有することはより面倒であり、従来のセッションはクラスターに優しいものではないため、一般的なアプリのバックエンドサービスはユーザーログイン情報を区別します。
誰もがJ2EEのセッションメカニズムを知っています。これは、使用するのに非常に便利で、従来のJava Webアプリケーションで非常に役立ちます。ただし、インターネットプロジェクトやクラスターで使用できるプロジェクトには、シリアル化の問題、同期遅延問題など、いくつかの問題があるため、セッションに似たクラスターの問題を解決できるツールが必要です。
プラン
キャッシュメカニズムを使用して、この問題を解決します。より人気のあるRedisはNOSQLメモリデータベースで、セッションデータの保存に非常に適したキャッシュ障害メカニズムがあります。トークン文字列は最初のリクエストでクライアントに返す必要があり、クライアントはこのトークンを使用して、将来リクエストするたびにアイデンティティを識別します。ビジネス開発について透明性を持つために、アプリの要求と応答によって作成されたパケットをカプセル化します。クライアントのHTTPリクエストツールクラスとサーバーのMVCフレームワークにいくつかのトリックを行う必要があります。クライアントのHTTPツールクラスの変更は非常にシンプルで、主にサーバーのプロトコルカプセル化です。
実装のアイデア
1。リクエスト応答メッセージプロトコルを策定します。
2。解析プロトコルはトークン文字列を処理します。
3. Redisを使用して保存して、トークンと対応するセッション情報を管理します。
4.セッション情報を保存および取得するためのAPIを提供します。
各ステップの実装計画について説明します。
1。リクエスト応答メッセージプロトコルを策定します。
メッセージプロトコルをカプセル化するため、パブリックフィールドとは何か、サービスフィールドとは何か、メッセージのデータ構造などを考慮する必要があります。
要求されたパブリックフィールドには、一般にトークン、バージョン、プラットフォーム、モデル、IMEI、アプリソースなどが含まれます。その中には、今回のトークンが主人公です。
応答の一般的なフィールドには、一般にトークン、結果ステータス(成功、失敗)、結果コード(コード)、結果情報などが含まれます。
パケットデータ構造の場合、JSONは一般的であり、良好な視覚化を持ち、バイト占有率が低いため、JSONを選択します。
リクエストメッセージは次のとおりで、ボディはログインしたユーザー名とパスワードなどのビジネス情報を保存します。
{"token": "クライアントトークン"、 / **クライアントビルドバージョン番号* / "バージョン":11、 / **クライアントプラットフォームタイプ* / "プラットフォーム": "iOS"、 / **クライアントデバイスモデル* / "machinemodel": "iphone 6s"、 "imei": "クライアント文字列番号(携帯電話)"、 " "key21": "value21"}、 "key3":[1、]}}レスポンシブメッセージ
{ /*成功* / "success":false、 / **すべてのリクエストはトークンに戻り、クライアントは各リクエストに対して最新のトークンを使用する必要があります* / "トークン":「現在のリクエストのためにサーバーを選択したトークン* / "failcode* /" failcode ":1、 / ** businessメッセージまたは失敗メッセージ* /" msg ":" necresed necase busient buseme busient busient busient buse buse be s serial }}2。解析プロトコルはトークン文字列を処理します。
サーバー側のMVCフレームワークには、SpringMVCフレームワークも使用します。これも一般的であり、説明されません。
当面の間、トークンの処理について言及しないでください。まず、パケットを策定した後にパラメーターを渡す方法。
要求情報はカプセル化されているため、SpringMVCフレームワークがコントローラーに必要なパラメーターを正しく挿入するためには、パケットを解析して変換する必要があります。
要求情報を解析するには、SpringMVCのパラメーターコンバーターをカスタマイズする必要があります。 HandlermethodadargumentResolverインターフェイスを実装することにより、パラメーターコンバーターを定義できます
RequestBodyResolverは、ResolvearGumentメソッドを実装し、パラメーターを注入します。次のコードはサンプルコードであり、直接使用しないでください。
@Override Public Object Resolveargument(MethodParameterパラメーター、ModelandViewContainer、nativewebrequest webrequest、webdatabinderfactory binderfactory)スロー{string bodystr = webrequest.getparameter(requestbodyparamname); if(stringutils.isnotblank(requestbodystr)){string paramname = paramenter.getParametername(); //コントローラークラスでパラメーター名を取得<? if(paramclass.equals(servicerequest.class))){// servicerequestは、packet servicerequest servicerequest = objectmapper.readvalue(jsonnode.traverse()、servicerequest.class)に対応するVoです。 ServiceRequestを返します; //このオブジェクトを返してパラメーターに注入してください、それはタイプに対応する必要があります。そうしないと、例外は簡単にキャッチされません} if(jsonnode!= null){//メッセージjsonnode paramjsonnode = jsonnode.findvalue(paramname); if(paramjsonnode!= null){return objectmapper.readValue(paramjsonnode.traverse()、paramclass); }}} nullを返します。 }SRPINGMVC構成ファイルに定義したパラメーターコンバーターを構成<MVC:Argument-Resolvers>
<MVC:argument-resolvers> <! - unified要求情報処理、Servicerequestからのデータの取得 - > <bean id = "requestbodyResolver"> <Property name = "objectmapper"> <bean> </bean> </property> <!- name = "RequestBodyParamName"> <value> requestBody </value> </property> </bean> </mvc:argument-resolvers>
これにより、メッセージ内のパラメーターをSpringMVCによって正しく識別できます。
次に、トークンを処理する必要があります。各リクエストを傍受するために、SRPINGMVCインターセプターを追加する必要があります。これは一般的な機能であり、詳細に説明されません。
Matcher M1 = pattern.compile( "/" token /"(.)/ if(m1.find()){token = m1.group(1);} tokenmappool.verifytoken(token); //トークンのパブリック処理を実行して検証するこのようにして、トークンを取得し、公開処理を行うことができます。
3. Redisを使用して保存して、トークンと対応するセッション情報を管理します。
実際、Redis操作ツールのクラスを作成するだけです。 Springはプロジェクトの主なフレームワークとして使用されており、Redisの多くの機能を使用していないため、Springが提供するCacheManager機能を直接使用します。
org.springframework.data.redis.cache.rediscachemanagerを構成します
<! - キャッシュマネージャーグローバル変数などは、アクセスに使用できます - > <bean id = "cachemanager"> <constructor-arg> <ref bean = "redistemplate"/> </> </</</</</</</</</</</> "value =" true "/> <プロパティ名=" value = ":@webserviceInterface"/> </bean> </property> <property name = "expires"> <! - キャッシュ有効性期間 - > <マップ> <エントリ> <キー> </</value> </key> <!
4.セッション情報を保存および取得するためのAPIを提供します。
上記の前戯を通して、トークンをほぼ処理しました。次に、トークン管理作業を実装します。
セッション情報を保存および取得するためにビジネス開発を便利にする必要があり、トークンは透明です。
java.util.hashmap; Import java.util.map; import org.apache.commons.logging.log; import org.apache.commons.logging.logfactory; import org.springframework.cache.cache; Import org.springframework.cache.valuewramper; org.springframework.cache.cache.valuewrapper; Import org.springframework.cache.cachemanager;/** * *クラス名:tokenmappoolbean *説明:トークンおよび関連情報コール処理クラス *修正記録: * @version v1.0 * @date niutor niutor niuxzz */public class log = logfactory.getlog(tokenmappoolbean.class); / **現在のリクエストに対応するトークン*/ private threadlocal <string> currentToken;プライベートキャッシュマネージャーのCachemanager;プライベート文字列cachename;プライベートトケンゲネレータートケンゲネレーター。 Public Tokenmappoolbean(Cachemanager Cachemanager、String Cachename、Tokengenerator tokengenerator){this.cachemanager = cachemanager; this.cachename = cachename; this.tokengenerator = tokengenerator; currentToken = new ThreadLocal <String>(); } /***トークンが合法の場合は、トークンを返します。新しいトークンを作成して戻り、 *トークンをthreadlocalに入れてtokenmapを初期化 * @param token * @return token */ public string verifytoken(string token){// log.info("CheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheC heCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheC verifyedtoken = null; if(tokengenerator.checktokenformat(token)){// log.info( "checktoken succuper:/"+token+"/" "); verifyedtoken = token;} (cache == null){新しいruntimeexception( "トークンが保存されているキャッシュプール、" + cachename)} cache.get VerifyedToken = currentToken.set(everifted); Cachemanager.getcache(cache == null){cache cache where where chachename: "+cachename} (cache.get(newtoken)!= null); // log.info( "トークンを正常に作成する:/"+newtoken+"/"を生成してみてください: "+count+" times ");}/** *パブリックオブジェクトgetattribute(cache = cachemanager.getcache(cachename); tokenmap = null(tokenMapwrapper! tokenmapwrapper.get();最新のものは、データのボリュームが大きすぎると、最適化されています。 Chachename: " + cachename);} valueWrapper tokenmapwrapper = cache.get(currenttoken.get()); map <string、object> tokenmap = null; if(tokenmapwrapper!= null){tokenmap =(map <string、object> objectwrapper.get verifytoken(currenttoken.get()); cache.put(currenttoken.get()、tokenmap); public void removetokenmap(string token){token == null){return} cachemer.getCache(cachename){throw new runtimeexcept( "cachename:" + cachename);キャッシュ(トークン); = cachename} gettokenerator()スレッドローカル変数は、リクエストがサーブレットコンテナ内のスレッドに対応し、リクエストのライフサイクル中に同じスレッドにあるため、ここで使用され、複数のスレッドがトークンマネージャーを同時に共有するため、このスレッドローカル変数はトークン文字列を保存する必要があります。
注:
1.各リクエストの先頭に、VerifyTokenメソッドへの呼び出しを呼び出す必要があります。そして、リクエストが完了した後、Clearがクリアするように呼び出されます。 (このバグは数日間私を悩ませ、会社のN開発チェックコードは見つかりませんでした。最後に、テスト後、404が発生したときにインターセプターが入力されなかったことがわかりました。
2.クライアントは、HTTPツールをカプセル化するときに各トークンを保存し、次のリクエストに使用する必要があります。同社のIOS開発はアウトソーシングを要求しましたが、アウトソーシングは必要に応じて行われませんでした。ログインしていない場合、トークンは保存されません。トークンが渡されるたびに、それはnullであり、各リクエストに対してトークンが作成され、サーバーは多数の役に立たないトークンを作成します。
使用
使用方法も非常に簡単です。以下は、カプセル化されたログインマネージャーです。 Login ManagerのToken Managerのアプリケーションを参照できます。
Import.apache.commons.logging.log; Import org.apache.commons.logging.logfactory; import org.springframework.cache.cache; import org.springframework.cache.cache.valuewrapper; Import org.springframework.cachemanger.cachemanager.cachemanager.cachemanger.cachemanger.名前:loginmanager *説明:loginmanager *レコードの変更: * @version v1.0 * @date 2016年7月19日 * @author niuxz * */public class loginmanager {private static final log = logfactory.getlog(loginmanager.class);プライベートキャッシュマネージャーのCachemanager;プライベート文字列cachename; Private Tokenmappoolbean tokenmappool; Public LoginManager(Cachemanager Cachemanager、String Cachename、tokenmappoolbean tokenmappool){this.cachemanager = cachemanager; this.cachename = cachename; this.tokenmappool = tokenmappool; } public void login(string userid){log.info( "user login:userid =" + userId);キャッシュcache = cachemanager.getCache(cachename); ValueWrapper ValueWrapper = cache.get(userId); string token =(string)(valuewrapper == null?null:valueWrapper.get()); tokenmappool.removetokenMap(token); //ログインレコードtokenmappool.setattribute(constants.logged_user_id、userid); cache.put(userid、tokenmappool.getoken()); } public void logoutcurrent(string phonetel){string curuserid = getCurrentuserid(); log.info( "user logout:userid =" + curuserid); tokenmappool.removetokenmap(tokenmappool.getoken()); // login if(curuserid!= null){cache cache = cachemanager.getcache(cachename); cache.evict(curuserid); cache.evict(Phonetel); }} / *: } public cachemanager getCacheManager(){return cachemanager; } public string getCachename(){return cachename; } public tokenmappoolbean gettokenMappool(){return tokenmappool; } public void setcachemanager(cachemanager cachemanager){this.cachemanager = cachemanager; } public void setCachename(string cachename){this.cachename = cachename; } public void setokenmappool(tokenmappoolbean tokenmappool){this.tokenmappool = tokenmappool; }}以下は、一般的なSMS検証コードインターフェイスです。一部のアプリケーションは、セッションを使用して検証コードを保存します。この方法を使用することはお勧めしません。保管セッションの欠点は非常に大きいです。それを見てください、それは私が書いたものではありませんでした
public void sendvalicodeByphonenum(String PhoneNum、String Hintmsg、String logsuffix){validatephonetimespace(); // 6ビットのランダム数文字列code = codeutil.getValidateCode()を取得します。 log.info(code + "------>" + phoneNum); // SMS検証コードを呼び出して、Interface retstatus retstatus = msgsendutils.sendsms(code + hintmsg、phoneNum)を送信します。 if(!retstatus.getisok()){log.info(retstatus.tostring());新しいThrowStodataException(Serviceresponsecode.fail_invalid_params、 "携帯電話検証コードが取得できなかったので、後でもう一度お試しください"); } //セッションtokenmappool.setattribute(constants.Validate_Phone、PhoneNum); tokenmappool.setattribute(constants.validate_phone_code、code.toString()); tokenmappool.setattribute(constants.send_code_wrongnu、0); tokenmappool.setattribute(constants.send_code_time、new date()。gettime()); log.info(logsuffix + phoneNum + "SMS検証コード:" + code); }処理応答
一部の学生は、そのような応答性のあるメッセージパッケージがあるかどうかを尋ねますか?
@RequestMapping( "Record")@ResponseBodyPublic Serviceresponse Record(String Message){string userid = loginmanager.getCurrentuserid(); messageboardservice.recordMessage(userId、message); return serviceresponsebuilder.buildsuccess(null);}その中で、Serviceresponseはカプセル化された応答パケットVOです。 SpringMVCの@ResponseBodyアノテーションを使用する必要があります。重要なのはこのビルダーです。
Import org.apache.commons.lang3.stringutils; Import com.niuxz.base.pojo.serviceresponse;インポートcom.niuxz.utils.spring.springcontextutil; Import com.niuxz.web.server.tokenmappoolbean; @date 2016年4月25日 * @author niuxz * */public class serviceresponsebuilder {/** *成功した応答メッセージを作成 * * * @param body * @return操作の成功 */public static serviceresponse buildsuccess(object body){(return new servicerespons SpringContextutil.getBean( "tokenmappool")).getToken()、 "Action Success"、Body); } / ** *成功した応答メッセージを作成 * * @param body * @@return操作が成功したServiceresponse * / public static serviceresponse buildsuccess(string token、object body){return new serviceresponse(token、 "action succugt"、body); } / ** *失敗した応答メッセージの作成 * * @param failcode * msg * @return操作に失敗したserviceresponse * / public static serviceresponse buildfail(int failcode、string msg){return buildfail(failcode、msg、null); } / ** *失敗した応答メッセージの作成 * * @param failcode * msg body * @return操作に失敗したserviceresponse * / public static serviceresponse buildfail(int failcode、string msg、object body){((tokenmappoolbean)springcontextil.getbean( ")。 stringutils.isnotblank(msg):「操作が失敗」、ボディ); }}静的ツールクラスの形式を使用するため、SpringからTokenMappool(Token Manager)オブジェクトに注入することはできません。その後、Springが提供するAPIを介して取得できます。次に、応答情報を作成するときは、tokenmappoolのgettoken()メソッドを直接呼び出します。この方法は、現在のスレッドにバインドされたトークン文字列を返します。繰り返しますが、リクエストが終了した後、手動で明確に呼び出すことが重要です(グローバルインターセプターを介して呼び出します)。
J2EEのセッションメカニズムを模倣したアプリバックエンドセッション情報管理の上記の例は、私があなたと共有するすべてのコンテンツです。参照を提供できることを願っています。wulin.comをもっとサポートできることを願っています。