Introduction du service de repos
Le service RESTful est un modèle architectural qui est devenu plus populaire ces dernières années. Son service Web léger joue le Native Get, Put, Post et Supprimer du protocole HTTP. Par rapport au SOAP complexe et XML-RPC, les services Web de Mode REST sont évidemment plus concis, et de plus en plus de services Web commencent à adopter la conception et la mise en œuvre du style de repos. Par exemple, Amazon.com fournit des services Web proches du style de repos pour les recherches de livres; Les services Web fournis par Yahoo sont également un style de repos. Le repos n'est pas toujours le bon choix. Il est devenu populaire comme méthode de conception de services Web qui s'appuient moins sur le middleware propriétaire, comme un serveur d'applications, que sur les méthodes basées sur SOAP et WSDL. Dans un sens, en mettant l'accent sur les premières normes Internet telles que URI et HTTP, REST est une régression de l'approche Web avant l'ère des grands serveurs d'applications.
Exemple comme indiqué dans la figure suivante:
La clé de l'utilisation du repos est de savoir comment abstraction des ressources. Plus l'abstraction est précise, meilleure est l'application du repos.
Principes clés du service de repos:
1. Donnez à tous les objets un ID
2. Connectez les objets ensemble
3. Utilisez des méthodes standard
4. Représentations multiples des ressources
5. Communication apatride
Cet article présente comment créer un cadre de service de repos simple basé sur Spring Boot, et comment implémenter l'authentification des services de repos via des annotations personnalisées
Construire un cadre
pom.xml
Tout d'abord, introduisez des dépendances connexes, utilisez MongoDB pour les bases de données et utilisez Redis pour le cache
Remarque: il n'y a pas de tomcat utilisé ici, mais Undertow
<dependency> <proupId> org.springframework.boot </rombasid> <artifactId> printemp-boot-starter </ artifactid> </dendency> <dependency> <proupId> org.springframework.boot </prouprid> <ptifactid> printemp-boot-starter-test </ptetifactid> <scope> Test </scope> </stiference> <cate> <GroupId> org.springframework.boot </proupId> <Artifactid> printemps-boot-starter-web </lefactive> <cglusions> <cusclusion> <proupId> org.springframework.boot </proupId> <Artifactid> Spring-Boot-starter-toMcat </ratifactid> </cusclusion> <dependency> <proupId> org.springframework.boot </proupId> <Artifactid> printemps-boot-starter-undertow </ artifactid> </deedency> <! - redis support -> <pedency> <proupId> org.springframework.boot </proupId> </ artifactid> spring-boot-starter-data-data-lidis </ artifactid> spring-boot-starter-data-data-lidis </ artifactid> spring-boot-starter-data-data-lidis </ artifactive> <! - MongoDB Support -> <Dedency> <GroupId> org.springFramework.boot </rolgled> <Artefactid> printemp-boot-starter-data-mongodb </letefactid> </dependency>
Introduire les services Web de support Spring-Boot-Starter-Web
L'introduction de Spring-Boot-Starter-Data-Redis et Spring-Boot-Starter-Data-MongoDB peut facilement utiliser MongoDB et Redis
Fichier de configuration
Fonctionne les profils
Afin de faciliter la distinction entre l'environnement de développement et l'environnement en ligne, la fonction des profils peut être utilisée pour l'ajouter en application.
spring.profiles.active=dev
Ajoutez ensuite l'application-dev.properties comme fichier de configuration de développement.
Configuration MONDB
Configurez simplement l'adresse de la base de données
printemps.data.mongodb.uri = mongodb: // ip: port / base de données? readPreference = primairePreferred
Configuration redis
Spring.redis.database = 0 # Redis Server Address Spring.redis.host = IP # Redis Server Connection Port Spring.redis.port = 6379 # Redis Server Connection Mot de passe (Par défaut est vide) Spring.redis.Password = # Nombre maximum de connexions dans le pool de connexions (Utilisation de valeurs négatives signifie sans limite) Spring.redis.pool.max-wait = -1 # Connexion maximale de ralenti dans le pool de connexion printemps.redis.pool.max-idle = 8 # Connexion inactive minimum dans le pool de connexion printemps.redis.pool.min-idle = 0 # délai de connexion (ms) spring.redis.timeout = 0
Accès aux données
mongdb
L'accès à Mongdb est très simple. Vous pouvez définir directement l'interface étend Mongorepository. De plus, il peut prendre en charge la syntaxe JPA, par exemple:
@ComponentPublic Interface UserRepository étend Mongorepository <utilisateur, entier> {public utilisateur findByUserName (String Username);}Lorsque vous l'utilisez, ajoutez simplement l'annotation @Autowired.
@ComponentPublic classe AuthService étend BasEservice {@Autowired userRepository userRepository; }Redis Access
Utilisez StringRedistemplate pour accéder directement à Redis
@ComponentPublic Class BaseService {@Autowired Mongotemplate Protected Mongotemplate; @Autowired Protected StringRedistemplate StringRedistemplate; }Stocker les données:
.StringRedistEmplate.opsForValue (). set (token_key, user.getID () + "", token_max_age, timeunit.seconds);
Supprimer les données:
StringRedIsTemplate.Delete (getFormattoken (AccessToken, plate-forme));
Services Web
Définissez une classe de contrôleur, ajoutez RestController et utilisez la mise à niveau de demande pour définir l'itinéraire URL
@RestControllerPublic classe AuthController étend BaseController {@RequestMapping (value = {"/"}, produit = "application / json; charset = utf-8", méthode = {requestMethod.get, requestMethod.Post}) @ResponseBody public String main () {return "bonjour world!";; }}Commencez maintenant, vous devriez pouvoir voir Hello World! C'est
Authentification du service
Mécanisme d'accès simple
Fournir une interface de connexion. Une fois l'authentification réussie, un AccessToken est généré. Lorsque vous accédez à l'interface à l'avenir, l'accès à l'accès est apporté avec lui. Le serveur utilise le AccessToken pour déterminer s'il s'agit d'un utilisateur légal.
Pour plus de commodité, vous pouvez enregistrer l'accès à redis et définir la période de validité.
String token = EncryptionUtils.sha256hex (string.format ("% s% s", user.getUsername (), system.currentTimemillis ())); String token_key = getFormattoken (token, plate-forme); this.stringredistemplate.opsforvalue (). set (token_key, user.getid () + "", token_max_age, timeunit.seconds);Authentification de l'identité interceptrice
Afin de faciliter l'authentification unifiée de l'identité, un intercepteur peut être créé en fonction du mécanisme intercepteur de Spring pour effectuer une authentification unifiée.
classe publique AuthCheckInterceptor implémente HandlerInterceptor {}Pour rendre l'intercepteur à prendre effet, une étape de plus est nécessaire pour ajouter la configuration:
@ConfigurationPublic Class SessionConfiguration étend webmvcconfigurerAdapter {@autowired authCheckInterceptor authCheckInterceptor; @Override public void addInterceptors (InterceptorRegistry Registry) {super.addInterceptors (registre); // Ajouter Interceptor Registry.AddInterceptor (AuthCheckInterceptor) .AddPathPatterns ("/ **"); }}Annotations d'authentification personnalisées
Afin d'affiner l'authentification de l'autorisation, par exemple, certaines interfaces ne sont accessibles que par des personnes ayant des autorisations spécifiques et peuvent être facilement résolues par le biais d'annotations personnalisées. Dans l'annotation personnalisée, ajoutez simplement des rôles.
/ ** * ANNOTATION DE VÉRIFICATION PERMISSIQUE * / @ Target (elementType.Method) @retention (RetentionPolicy.Runtime) @DocumendedPublic @Interface AuthCheck {/ ** * Liste des rôles * @return * / String [] rôles () Default {};};Logique d'inspection:
Tant que l'interface est ajoutée avec une annotation AuthCheck, elle doit être enregistrée à l'utilisateur
Si les rôles sont spécifiés, en plus de se connecter, l'utilisateur doit également avoir le rôle correspondant.
String [] ignoreurls = new String [] {"/User/.*", "/cat/*", "/app/.*", "/ error"}; Boolean Prehandle (httpservletRequest httpservletRequest, httpservletResponse httpservletResponse, handler d'objet) lève une exception {// 0 vérifie les paramètres publics if (! checkParams ("plateforme", httSservletRequest, httSservletResponse)) {return false; } // 1. Ignorez la chaîne de vérification URL URL = httpservletRequest.getRequeSturi (). ToString (); for (String ignoreurl: ignoreUrls) {if (url.matches (ignoreurl)) {return true; }} // 2. Query Verification Annotation handLerMethod handlerMethod = (handlerMethod) Handler; Méthode méthode = handlerMethod.getMethod (); // Query Annotation AuthCheck AuthCheck = Method.GetAnnotation (AuthCheck.class); if (authCheck == null) {// pas d'annotation, pas de retour true; } // 3. S'il y a une annotation, vérifiez d'abord AccessToken if (! CheckParams ("AccessToken", httpservletRequest, httpServletResponse)) {return false; } // Vérifiez si le jeton expire Integer Userid = AuthService.getUserIdFromToken (httpServletRequest.getParameter ("AccessToken"), httpServletRequest.getParameter ("Platform")); if (userid == null) {logger.debug ("accessToken") timeout "); output (reverseResult.builder.error (" AccessToken expire if (authCheck.Roles ()! = null && authCheck.Roles (). Échec if (! Ismatch) {return false;}} return true; Encapsulation du résultat de la réponse du service
Ajoutez un constructeur pour faciliter la génération de résultats finaux
classe publique RéponseResult {Builder de classe statique publique {RéponseResult ResponseResult; Map <string, objet> dataMap = maps.newhashmap (); public Builder () {this.ResponSeResult = new AbenseResult (); } public Builder (String State) {this.ResponSeResult = new AbenseResult (état); } public static builder newbuilder () {return new Builder (); } public static Builder Success () {return new Builder ("Success"); } Public Static Builder Error (Message de chaîne) {Builder Builder = new Builder ("Error"); builder.ResponSeResult.seTerror (message); Builder de retour; } public Builder append (clé de chaîne, données d'objet) {this.datamap.put (key, data); retourner ceci; } / ** * Set List Data * @param Datas Données * @Return * / Public Builder setListData (list <?> Datas) {this.datamap.put ("result", datas); this.datamap.put ("total", datas.size ()); retourner ceci; } public Builder setData (données d'objet) {this.datamap.clear (); this.ResponSeresult.setData (données); retourner ceci; } boolean wrapData = false; / ** * Enveloppez les données dans les données * @param wrapData * @return * / public Builder Wrap (boolean wrapData) {this.wrapdata = wrapData; retourner ceci; } public String build () {jsonObject JSONObject = new JSONObject (); jsonObject.put ("State", this.ResponSeResult.getState ()); if (this.ResponSeResult.getState (). equals ("error")) {jsonObject.put ("error", this.ResponSeResult.GetTerror ()); } if (this.ResponSeResult.getData ()! = null) {jsonObject.put ("data", json.tojson (this.responseResult.getData ())); } else if (dataMap.size ()> 0) {if (wrapData) {jsonObject data = new JSONObject (); dataMap.ForEach ((key, valeur) -> {data.put (key, valeur);}); jsonObject.put ("data", données); } else {dataMap.ForEach ((key, value) -> {jsonObject.put (key, value);}); }} return jsonObject.tojSontring (); }} État de chaîne privée; données d'objets privés; Erreur de chaîne privée; public String getError () {return error; } public void seterror (String error) {this.error = error; } publique ReverseReSult () {} public RepainceResult (String rc) {this.state = rc; } / ** * RETOURNER Lorsque réussi * @param rc * @param result * / public fondeResult (String rc, objet résultat) {this.state = rc; this.data = résultat; } public String getState () {return State; } public void setState (String State) {this.state = state; } Objet public getData () {return data; } public void setData (objet Data) {this.data = data; }} Être plus élégant lorsque vous appelez
@RequestMapping (value = {"/ user / ligin", "/ pc / user / ligin"}, produmes = "application / json; charset = utf-8", method = {requestMethod.get, requestMethod.post}) @ResponseBody Public Strang (string username, chaîne de chaîne, pose de mots); if (user! = null) {// Connectez-vous à String token = AuthService.upDateToken (utilisateur, plate-forme); return reverseResult.builder .sucCess () .Apnd ("AccessToken", jeton) .APPEND ("userId", user.getId ()) .build (); } return reverseresult.builder.error ("L'utilisateur n'existe pas ou le mot de passe est mauvais"). build (); } Erreur de chaîne protégée (message de chaîne) {return reverseResult.builder.Error (message) .build (); } Protected String Success () {return reverseResult.Builder .SucCess () .Build (); } Protected String SuccessDatalist (list <?> data) {return reverseResult.builder .success () .wrap (true) // data package.setListData (data) .build (); }Résumer
Ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.