Dieser Artikel enthält nur Ideen und bietet keine spezifische und vollständige Implementierung (der Blogger ist zu faul, um es zu klären). Wenn Sie Fragen haben oder wissen möchten, können Sie eine private Nachricht oder einen Kommentar senden.
Hintergrund
In traditionellen Java-Web-kleinen und mittelgroßen Projekten wird die Sitzung im Allgemeinen vorübergehend Sitzungsinformationen wie die Identitätsinformationen des Protokolls verwendet. Dieser Mechanismus wird implementiert, indem der Cookie -Mechanismus von HTTP ausgeliehen wird. Es ist jedoch problematisch, dass die App Cookie -Informationen jedes Mal speichern und freigeben, wenn sie anfordert, und die herkömmliche Sitzung ist nicht clusterfreundlich, sodass die allgemeinen Backend -Dienste für App -Backend -Dienste Token verwenden, um Benutzeranmeldeinformationen zu unterscheiden.
Jeder kennt den Sitzungsmechanismus von J2EE, der sehr bequem zu verwenden ist und in herkömmlichen Java -Webanwendungen sehr nützlich ist. Einige Projekte, die in Internetprojekten oder Clustern verwendet werden können, haben jedoch einige Probleme, wie Serialisierungsprobleme, Probleme mit Synchronisation Verzögerungen usw., daher benötigen wir ein Tool, das Clusterprobleme lösen kann, die den Sitzungen ähneln.
planen
Wir verwenden den Cache -Mechanismus, um dieses Problem zu lösen. Die beliebtere Redis ist eine NoSQL -Speicherdatenbank und verfügt über einen Cache -Fehlermechanismus, der für die Speicherung von Sitzungsdaten sehr geeignet ist. Die Token -Zeichenfolge muss bei der ersten Anfrage an den Client zurückgegeben werden, und der Kunde verwendet dieses Token, um die Identität jedes Mal in Zukunft zu identifizieren. Um die Geschäftsentwicklung transparent zu machen, verkapulieren wir die Pakete, die durch die Anfrage und Antwort der App erstellt wurden. Wir müssen nur einige Tricks zur HTTP -Anforderungs -Toolklasse des Clients und im MVC -Framework des Servers machen. Die Änderung der HTTP -Toolklasse des Clients ist sehr einfach, hauptsächlich die Protokollkapselung des Servers.
Implementierungsideen
1. Formulieren Sie ein Protokoll zur Requestantwortmeldung.
2. Das Parsing -Protokoll verarbeitet Token -Saiten.
3.. Verwenden Sie Redis, um Token und entsprechende Sitzungsinformationen zu speichern.
4. Geben Sie eine API zum Speichern und Erhalten von Sitzungsinformationen an.
Wir werden den Implementierungsplan jedes Schrittschritts erläutern.
1. Formulieren Sie ein Protokoll zur Requestantwortmeldung.
Da Sie das Nachrichtenprotokoll zusammenfassen möchten, müssen Sie überlegen, was ein öffentliches Feld ist, was ein Dienstfeld, die Datenstruktur der Nachricht usw. ist.
Zu den gefragten öffentlichen Feldern gehören im Allgemeinen Token, Version, Plattform, Modell, IMEI, App Source usw., unter dem Token der Protagonist unserer diesmal ist.
Die gemeinsamen Felder der Antwort umfassen im Allgemeinen Token, Ergebnisstatus (Erfolg, Scheitern), Ergebniscode (Code), Ergebnisinformationen usw.
Für die Paketdatenstruktur wählen wir JSON, weil JSON gemeinsam ist, eine gute Visualisierung hat und eine geringe Beschäftigung von Byte hat.
Die Anforderungsnachricht lautet wie folgt, und die Body speichert Geschäftsinformationen, wie z. B. den angemeldeten Benutzernamen und Passwort usw., usw.
{"Token": "Client Token", / ** Client -Build -Versionsnummer* / "Version": 11, / ** Client -Plattform Typ* / "Plattform": "iOS", / ** Client -Gerätemodell* / "machinemodel": "iPhone 6s", "IMEI": "Client -String -Nummer (Mobile Telefon). {"key21": "value21"}, "key3": [1,]}}Reaktionsschnelle Nachricht
{ /** Erfolg* /"Erfolg": Falsch, /** Jede Anfrage wird zu einem Token zurückgeführt, und der Client sollte das neueste Token für jede Anfrage verwenden* /"Token": "Der Server -Token für die aktuelle Anfrage", /** Fehlgeschlagener Code* /"Failcode": 1, /** Geschäftsnachrichten oder MSGE -MSGE -MSGE -MSGEL -MSGEL -MSGE -MSGEL -MSGEL -MSGEL -MSGE ": "Körper": null}}2. Das Parsing -Protokoll verarbeitet Token -Saiten.
Für das serverseitige MVC-Framework verwenden wir das SpringMVC-Framework, das ebenfalls häufig ist und nicht beschrieben wird.
Erwähnen wir die Verarbeitung von Token vorerst nicht. Erstens, wie man Parameter nach der Formulierung des Pakets übergibt.
Da die Anforderungsinformationen eingekapselt sind, müssen wir die Pakete analysieren und konvertieren, damit das SpringMVC -Framework die Parameter, die wir im Controller benötigen, korrekt injizieren müssen.
Um die Anforderungsinformationen zu analysieren, müssen wir den Parameterkonverter von SpringMVC anpassen. Durch die Implementierung der HandleMethodargumentResolver -Schnittstelle können wir einen Parameterwandler definieren
RequestBodyResolver implementiert die ResolVearGument -Methode und injiziert Parameter. Der folgende Code ist ein Beispielcode und verwenden ihn nicht direkt.
@Override Public Object ResolVearGument (MethodParameter Parameter, modelAndViewContainer Mavcontainer, natives WebRequest Webrequest, WebDatabinderFactory Binderfactory) löst Ausnahme aus {String RequestBodyStr = WebRequest.getParameter (Requestbodyparamnamen). Get it if (Stringutils.isnotblank (RequestBodyStr)) {String paramname = parameter.getParametername (); // Parametername in Controller -Klasse <?> Parameter = Parameter.GetParameterType (); // Parameter -Typ in Controller/* Pares Packets Through Json Tools Class*/JsonNode Jsonnode teur =/Jsonnode JONNODE ObjectMapper.readtree (RequestBodyStr); if (paramclass.equals (serviceRequest.class)) {// serviceRequest ist das VO, das dem Anfragepaket servicerequest servicerequest = ObjectMapper.readValue (jsonnode.traverse (), servicerequest.class) entspricht; Return serviceRequest zurückgeben; // Rückgabe dieses Objekts, um sie in den Parameter zu injizieren. Es muss dem Typ entsprechen, andernfalls ist die Ausnahme nicht leicht zu erfassen} if (jsonNode! if (paramjsonNode! }}} return null; }Konfigurieren Sie den Parameterkonverter, den Sie in der SRPingMVC-Konfigurationsdatei definiert haben <MVC: Argument-Resolver>
<MVC: Argument-Resolver> <!-Unified Request Information Processing, Abrufen von Daten von servicerequest-> <bean id = "RequestBodyResolver"> <Eigenschaft name = "ObjectMapper"> <bean> </bean> </Property> <!-Der Feldname, der an Dienste entsprechend der Dienste entspricht. name = "RequestBodyparamName"> <wert> RequestBody </value> </property> </bean> </mvc: Argument-Resolver>
Auf diese Weise können die Parameter in der Nachricht durch SpringMVC korrekt identifiziert werden.
Als nächstes müssen wir das Token verarbeiten. Wir müssen einen SrppingMVC -Interceptor hinzufügen, um jede Anfrage abzufangen. Dies ist eine gemeinsame Funktion und wird nicht ausführlich beschrieben.
Matcher m1 = muster.comPile ("/" Token /":/"(.*?/ ""). Match (RequestBodyStr); if (m1.find ()) {token = m1.group (1);} tokenmappool.verifyToken (token); // Führen Sie die öffentliche Verarbeitung von Token durch und überprüfen Sie diesAuf diese Weise können Sie das Token erhalten und Sie können öffentliche Verarbeitung durchführen.
3.. Verwenden Sie Redis, um Token und entsprechende Sitzungsinformationen zu speichern.
Tatsächlich schreibt es nur eine Redis -Operation -Toolklasse. Da die Frühling als Hauptgerüst des Projekts verwendet wird und wir nicht viele Funktionen von Redis verwenden, verwenden wir die von Spring bereitgestellte Cachemanager -Funktion direkt.
Konfigurieren Sie org
<!-Cache Manager Globale Variablen usw. können zum Zugriff verwendet werden-> <bean id = "cachemanager"> <constructor-arg> <ref bean = "redistemplate"/> </constructor-arg> <Property name = "useprefix" value = "true"/> <Property name = " value = ":@webServiceInterface"/> </bean> </property> <Eigenschaft name = "expires"> <!-Cache-Gültigkeitsdauer-> <Map> <eintrag> <etaste> <wert> tokenpoolcache </value> </key> <!-Tokenpool-Cache-Cache-Name-MAGE> 2592000 </value> </value> </value> </value> </value> </value> </value> </value> </value> </value> </value> </value> </value> <!
4. Geben Sie eine API zum Speichern und Erhalten von Sitzungsinformationen an.
Durch das obige Vorspiel haben wir das Token fast verarbeitet. Als nächstes werden wir die Token -Management -Arbeit implementieren.
Wir müssen die Geschäftsentwicklung bequem machen, um Sitzungsinformationen zu sparen und zu erhalten, und Token sind transparent.
Import Java.util.hashMap; Import java.util.map; import org.apache.commons.logging.log; import org.apache.commons.logging.logfactory; import org.springframework.cache.cache; org.springframework.cache.cache.ValueWrapper; org.springFramework.cache.cachemanager;/** * * Klasse Name: tokenmappoolbean * Beschreibung: Token und verwandte Informationsanruf -Verarbeitungsklasse * Modification Record: * @Version v1.0 * @date April 22, 2016 * @Author Niox Niox Nioxz */öffentlich -öffentlich -Class -Class @date April 22, 2016 * @AUTHOR NOUTHOR NiUX Nixz */öffentlich -öffentlich -klasse upon 22, 2016 * @Author Niox Nioxz */öffentlich -öffentlich -klasse @date acrux @ @@Author Niox Nioxz */öffentlich klassifizieren apr. Finale log log = logFactory.getLog (tokenmappoolbean.class); / ** Token entspricht der aktuellen Anforderung*/ private ThreadLocal <String> currentToken; Privat CacheManager CacheManager; private Zeichenfolge Cachenname; privates Tokener -Tokenerator; public tokenmappoolbean (CacheManager CacheManager, String CacheName, Tokenerator -Tokenerator) {this.cachemanager = CacheManager; this.cacheName = cacheName; this.tokengeerator = tokenenerator; currentToken = new ThreadLocal <string> (); } /*** Wenn das Token legal ist, Rückgabe -Token. Erstellen Sie ein neues Token und kehren Sie zurück. log.info("CheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheC heCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheC verifyedToken = null; CacheManager.getCache (CacheName); ||. {Cache cache = cachemanager.getCache (CacheName); (cache.get (newToken)! = null); Sitzung */ öffentliches Objekt Getattribute (String -Schlüssel) {Cache Cache = CacheManager.getCache (CacheName); Objekt> tokenmap = null; (MAP <String, Objekt>) tokenmapWrapper.get (); Token, <br> * Tokenmap ist möglicherweise nicht die neueste, was den Datenverlust verursacht. RunTimeException ("kann nicht den Cache -Pool erhalten, in dem das Token gespeichert ist, ChacheName:" + cachename); tokenmapWrapper.get (); ", value =" + value + ")"); CurrentToken.get (); CacheName); } public void setCacheName (String CacheName) {this.cacheName = cacheName; currentToken.remove ();Die ThreadLocal -Variable wird hier verwendet, da eine Anforderung einem Thread im Servlet -Container entspricht und sich während des Lebenszyklus einer Anforderung im selben Thread befindet. Mehrere Threads teilen den Token -Manager gleichzeitig, sodass diese lokale Variable von Thread erforderlich ist, um die Token -Zeichenfolge zu speichern.
Anmerkungen:
1. Der Aufruf zur Überprüfungsmethode muss zu Beginn jeder Anfrage aufgerufen werden. Und nachdem die Anfrage abgeschlossen ist, wird Clear aufgerufen, um zu klären, damit die Verifizierung nicht zum nächsten Mal ausgeführt werden soll, aber das Token wird bei Rückkehr aus ThreadLocal herausgenommen. (Dieser Fehler hat mich seit mehreren Tagen gestört, und die N -Entwicklungscodes des Unternehmens wurden nicht gefunden. Schließlich wurde nach dem Testen festgestellt, dass der Interceptor nicht eingegeben wurde, als 404 die Verifizierungsmethode nicht bezeichnete, die das Token in der zurückgegebenen Ausnahmeinformationen zum letzten Zeitpunkt verursachte.
2. Der Client muss jedes Token speichern, wenn er das HTTP -Tool zusammenfasst und es für die nächste Anforderung verwenden. Die iOS -Entwicklung des Unternehmens forderte das Outsourcing auf, aber das Outsourcing lief nicht nach Bedarf. Wenn nicht angemeldet ist, wird das Token nicht gerettet. Jedes Mal, wenn das Token übergeben wird, ist es null, was dazu führt, dass ein Token für jede Anforderung erstellt wird und der Server eine große Anzahl nutzloser Token erstellt.
verwenden
Die Verwendungsmethode ist auch sehr einfach. Das Folgende ist der eingekapselte Login -Manager. Sie können sich auf die Anwendung von Token Manager für Login Manager beziehen.
import org.apache.commons.logging.log; import org.apache.commons.logging.logfactory; import org.springframework Klassenname: LoginManager * Beschreibung: LoginManager * Datensatz ändern: * @Version v1.0 * @date 19. Juli 2016 * @author niuxz * * */public class loginmanager {private statische endgültige log log = logFactory.getLog (LoginManager.Class); Privat CacheManager CacheManager; private Zeichenfolge Cachenname; 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 ("Benutzeranmeldung: userID =" + userId); Cache Cache = CacheManager.getCache (CacheName); ValueWrapper ValueWrapper = cache.get (userID); String token = (String) (valueWrapper == null? Null: valueWrapper.get ()); tokenmappool.removeTokenMap (token); // Anmeldedatensatz, bevor Sie tokenmappool.setAttribute abmelden (constants.logged_user_id, userId); cache.put (userId, tokenmappool.getToken ()); } public void logoutcurrent (String -Phonetel) {String curuSerId = getCurrentUSerId (); log.info ("Benutzer logout: userID =" + curuSerId); tokenmappool.removeTokenMap (tokenmappool.getToken ()); // Login if (curuSerId! cache.evict (curUserid); Cache.evict (Phonetel); }} / ** * Die ID des aktuellen Benutzers erhalten * @return * / public String getCurrentUSerId () {return (String) tokenmappool.getAttribute (constants.logged_user_id); } 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 sesskenmappool (tokenmappoolbean tokenmappool) {this.tokenMappool = tokenmappool; }}Unten finden Sie eine gemeinsame SMS -Verifizierungscode -Schnittstelle. Einige Anwendungen verwenden auch die Sitzung, um Überprüfungscodes zu speichern. Ich empfehle nicht, diese Methode zu verwenden. Die Nachteile der Aufbewahrungssitzungen sind ziemlich groß. Schau es dir einfach an, es war nicht das, was ich geschrieben habe
public void sendValicodeByphonenum (String -Phonenum, String Hintmsg, String LogSuffix) {validatePhonetimeSpace (); // 6-Bit-Zufallszahlenstring-Code abrufen = codeUtil.getValidateCode (); log.info (Code + "------>" + Phonenum); // Rufen Sie den SMS -Verifizierungscode an, um die Schnittstelle retstatus retstatus = msgsendutils.sendss (Code + Hintmsg, Phonenum) zu senden. if (! retstatus.getisok ()) {log.info (retstatus.tostring ()); Wurf neu throwStodataException (serviceresponScode.fail_invalid_params, "Der Verifizierungscode des Mobiltelefons hat es nicht erhalten, bitte versuchen Sie es später erneut"); } // Sitzung tokenmappool.setAttribute (constants.validate_phone, Phonenum); tokenmappool.setAttribute (constants.validate_phone_code, code.toString ()); tokenmappool.setAttribute (constants.send_code_wongnu, 0); tokenmappool.setAttribute (constants.send_code_time, neues Datum (). GetTime ()); log.info (logSuffix + Phonenum + "SMS -Verifizierungscode:" + Code); }Verarbeitungsantwort
Einige Schüler werden sich fragen, ob es eine so reaktionsschnelle Nachrichtenverpackung gibt.
@RequestMapping ("record")@responseBodypublic ServicerePonsed (String -Nachricht) {String userId = loginManager.getCurrentUSerID (); messageBoardService.recordMessage (userId, message); return serviceresponseBuilder.buildsuccess (null);}Unter ihnen ist ServicerePonse das eingekapselte Antwortpaket VO. Wir müssen nur die @ResponseBody -Annotation von SpringMVC verwenden. Der Schlüssel ist dieser Baumeister.
import org.apache.commons.lang3.stringutils; import com.niuxz.base.pojo.serviceresponse; import com.niuxz.utils.spring.springcontextil; Import Com.niuxz.web.Server.Token.Token. * @date 25. April 2016 * @Author Niuxz * * */public class serviceresponseBuilder {/** * Erstellen Sie eine erfolgreiche Antwortnachricht * * @param body * @return eine Servicersponse mit erfolgreicher Operation */public static serviceresponse buildsuccess (Objektkörper) {(((to Kekenmapper) zurückzusetzen) (((to Kekenmapper) zurückgeben) (((to Kekenmapper) zurückgeben) ((((to kenMapp) zurückgeben) (((to Keenmappool) zurückgeben) (((to Keenmappool) zurückgeben) ((((to kenmappool) zurückzusetzen) (((to kenmappool) zurückgeben) (((to kencerepool SpringContextUtil.getbean ("tokenmappool")) .GetToken (), "Action Success", Körper); } / ** * Erstellen Sie eine erfolgreiche Antwortmeldung * * @param Body * @return eine ServicerEsponse mit erfolgreicher Operation * / public statische ServicerePronse Buildsuccess (String Token, Object Body) {Neue Serviceresponse zurückgeben (Token, "Aktion erfolgreich", Körper); } / ** * Erstellen Sie eine fehlgeschlagene Antwortnachricht * * @param failCode * msg * @return eine serviceresponse, die fehlgeschlagen ist. } / ** * Erstellen Sie eine fehlgeschlagene Antwortnachricht * * @param failCode * msg body * @return eine serviceresponse, die fehlgeschlagen ist * / public static servicerePone buildfail (int failcode, String msg, Object Body) {return New servicerePress ((tokenmappoolbean), fehlcontextil.getion () (tokenmappoolBean). Stringutils.isnotblank (MSG)? }}Da wir die Form einer statischen Werkzeugklasse verwenden, können wir das Tokenmappool -Objekt (Token -Manager) durch den Frühling nicht injizieren, und dann können wir es durch die API erhalten, die von Frühling bereitgestellt wird. Wenn Sie dann die Antwortinformationen erstellen, rufen Sie dann direkt die GetToken () -Methode von TokenMappool auf. Diese Methode gibt die an den aktuelle Thread gebundene Token -Zeichenfolge zurück. Auch hier ist es wichtig, Clear manuell nach Ablauf der Anfrage zu rufen (ich rufe es über den globalen Interceptor an).
Das obige Beispiel für die App -Backend -Sitzungsverwaltung des App, die den Sitzungsmechanismus von J2ee nachahmt, ist der gesamte Inhalt, den ich mit Ihnen teile. Ich hoffe, Sie können Ihnen eine Referenz geben und ich hoffe, Sie können wulin.com mehr unterstützen.