Cet article comprend comment les filtres et les intercepteurs fonctionnent grâce à un exemple de pratique de certification de sécurité simple.
De nombreux articles associent des filtres, des intercepteurs et des auditeurs avec le printemps pour expliquer, et croient que les filtres, les intercepteurs et les auditeurs sont des fonctions de composants largement utilisées fournies par le printemps.
Mais à proprement parler, les filtres et les auditeurs appartiennent à l'API Servlet et n'ont rien à voir avec le printemps.
Étant donné que le filtre hérite de l'interface javax.servlet.filter et que l'écouteur hérite de l'interface javax.servlet.servletContextListener, seul l'intercepteur hérite de l'interface org.springframework.web.servlet.handlerInterceptor.
L'organigramme ci-dessus est référencé à partir des informations en ligne, et une image vaut mille mots. Après avoir lu cet article, vous aurez une compréhension plus approfondie du processus d'appel des filtres et des intercepteurs.
1. Idées de conception de certification de sécurité
Parfois, lorsque les réseaux internes et externes appellent les API, les exigences de sécurité pour différentes exigences. Dans de nombreux cas, les différentes restrictions sur les réseaux externes appellent les API ne sont pas nécessaires dans l'intranet. Cependant, lors du déploiement de passerelles, les API à appeler par des réseaux internes et externes peuvent être déployés ensemble en raison de problèmes de coût et de complexité.
Pour réaliser la sécurité de l'interface de repos, cela peut être fait via des cadres matures tels que Spring Security ou Shiro.
Cependant, comme les cadres de sécurité sont souvent complexes (j'ai compté la sécurité du ressort, il y a environ 11 modules de base et la quantité de code source de Shiro est également assez étonnante). Dans le même temps, des configurations complexes peuvent être introduites (peut-elle rendre les gens plus agréables), ce qui n'est pas propice au développement flexible et rapide, au déploiement et à l'étude des problèmes des équipes petites et moyennes.
De nombreuses équipes construisent leurs propres roues pour obtenir une certification de sécurité. Cet exemple de certification simple dans cet article fait référence à l'ancienne équipe de développement d'usine où je suis, et peut être considéré comme un service de certification de sécurité basé sur des jetons.
L'idée de conception générale est la suivante:
1. Personnalisez l'en-tête de demande HTTP. Chaque fois que l'API est appelée, une valeur de jeton est transmise dans l'en-tête de demande.
2. Placez le jeton dans le cache (comme Redis) et définissez le temps d'expiration de différentes politiques selon différentes entreprises et API.
3. Le jeton peut définir les listes blanches et les listes noires, ce qui peut limiter la fréquence des appels d'API, faciliter le développement et les tests, faciliter le traitement d'urgence des anomalies, et même fermer temporairement l'API.
4. L'appel de réseau externe doit être envoyé en jetons. Le jeton peut être lié à l'utilisateur, tel que chaque fois que vous ouvrez la page ou connectez-vous pour générer un en-tête de demande d'écriture de jeton, la page vérifie la validité des cookies et des jetons, etc.
Il y a deux concepts dans le cadre de sécurité Spring, à savoir l'authentification et l'autorisation . L'authentification fait référence aux utilisateurs qui peuvent accéder au système, tandis que l'autorisation est une ressource à laquelle les utilisateurs peuvent accéder.
Pour atteindre les exigences d'authentification de sécurité simples ci-dessus, vous devrez peut-être créer indépendamment un service de jeton pour vous assurer que le jeton est mondialement unique. Les modules qui peuvent inclure des générateurs de flux personnalisés, du CRM, du chiffrement et du déchiffrement, des journaux, des statistiques de l'API, des caches, etc., mais ils sont en fait faiblement liés aux utilisateurs (CRM). Certains services publics liés aux utilisateurs, tels que SMS et les services de messagerie que nous utilisons souvent, peuvent également résoudre les appels de sécurité via le mécanisme de jeton.
En résumé, la certification de sécurité simple dans cet article est en fait un peu différente de l'authentification et de l'autorisation fournies par le cadre de sécurité Spring. Bien sûr, cette méthode de traitement "de sécurité" n'est pas nouvelle pour les professionnels, mais il peut bloquer un grand nombre d'utilisateurs novices de l'extérieur.
2. Personnaliser le filtre
Semblable à Spring MVC, Spring Boot fournit de nombreux filtres (filtres) de servlet à utiliser, et il ajoute automatiquement certains filtres couramment utilisés, tels que CaractoScodingFilter (utilisé pour gérer les problèmes de codage), HiddenHttpMethodFilter (fonction HTTP cachée), HTTPPUTformContentFilter (Forme Forme Treat) Fonctions communes, telles que l'enregistrement des journaux, la détermination de la connexion, la vérification de l'autorisation, etc.
1. En-tête de demande personnalisé
C'est très simple, ajoutez un en-tête de demande personnalisé AuthToken dans l'en-tête de demande:
@Requestmapping (value = "/ getInfoByid", méthode = requestMethod.post) @apioperation ("Informations sur le produit de requête basée sur l'ID du produit") @apiimplicitParams ({@apiimplicitParam (paramtype = "Header", name = "Authtoken") GetGoodsByGoodSidResponse GetGoodsByGooDSID (@RequestHeader String AuthToken, @Requestbody GetGoodsByGoodsIdRequest) {return _GoodsapiService.getGoodsByGooSID (request); } getgoodsbygoodsidLe champ AuthToken modifié par @RequestHeader peut être affiché sous un cadre comme Swagger.
Après l'appel, vous pouvez voir l'en-tête de demande en fonction de l'outil HTTP. L'exemple de cet article est AuthToken (différent du jeton de certains frameworks):
Remarque: De nombreux outils HTTPClient prennent en charge les en-têtes de demande de transmission dynamique, tels que RestTemplate.
2. Implémenter le filtre
Il existe trois méthodes dans l'interface filtrante, à savoir init, dofilter et destory. Lorsque vous voyez le nom, vous connaîtrez probablement leurs principales utilisations. Habituellement, nous avons juste besoin de traiter les demandes HTTP dans la méthode Dofilter:
package com.power.demo.controller.filter; import com.power.demo.common.appconst; import com.power.demo.common.bizresult; import com.power.demo.service.contract.AuthtokenService. org.springframework.beans.factory.annotation.autowired; import org.springframework.sterreotype. AuthTokenService AuthTokenService; @Override public void init (FilterConfig var1) lève Servlexception {} @Override public void dofilter (ServletRequest Request, ServletResponse Response, filterchain chaîne) lance ioexception, servlexception) request; String token = req.getheader (appcconst.auth_token); BizResult <string> bizResult = AuthTokenService.PowerCheck (token); System.out.println (serializeUtil.serialize (bizresult)); if (bizResult.getSok () == true) {PowerLogger.info ("Auth Token Filter Passement"); chain.dofilter (demande, réponse); } else {Throw New Servlexception (bizresult.getMessage ()); }} @Override public void destre () {}} authtokenfilterNotez que du point de vue de la hiérarchie réelle, la plupart des choses qui traitent plus de couches d'expression sont traitées. Il n'est pas recommandé d'utiliser directement la couche d'accès aux données dans le filtre. Bien que j'ai vu un tel code à plusieurs reprises dans de nombreux anciens projets anciens il y a un an ou deux, et il y a des précédents pour écrire de cette façon dans le livre << Spring Practical >>.
3. Service de certification
Ceci est la principale logique commerciale. L'exemple de code est juste un moyen simple d'écrire des idées et ne doit pas être utilisé facilement dans les environnements de production:
package com.power.demo.service.impl; importer com.power.demo.cache.powerCacheBuilder; import com.power.demo.common.bizresult; import com.power.demo.service.contract.annotation. org.springframework.sterreotype.Component; import org.springframework.util.stringutils; @componentpublic classe AuthTokenServiceImpl implémente AuthTokenService {@Autowired Private PowerCacheBuilder CacheBuilder; / * * Vérifiez si le jeton d'en-tête de demande est légal * * / @Override public bizresult <string> powercheck (String token) {bizresult <string> bizresult = new bizresult <> (true, "vérification passée"); System.out.println ("La valeur du token est:" + token); if (stringUtils.isempty (token) == true) {bizresult.setfail ("AuthToken est vide"); retour bizresult; } // Traitement BlackList bizResult = CheckForbidList (Token); if (bizresult.getSok () == false) {return bizResult; } // traitement de la liste blanche bizresult = CheckAlList (jeton); if (bizresult.getSok () == false) {return bizResult; } String key = string.format ("power.authTokenService.% S", token); //cacheBuilder.set(key, token); //cacheBuilder.set(key, token.touppercase ()); // Fetch String existtOken à partir du cache = cachebuilder.get (key); if (stringUtils.iSempty (existtoken) == true) {bizresult.setfail (string.format ("this authtoken:% s", token)); retour bizresult; } // Comparez si le jeton est le même booléen isEqual = token.equals (existik); if (isEqual == false) {bizresult.setfail (String.format ("illégal authtoken:% s", token)); retour bizresult; } // faire quelque chose de retour bizresult; }} AuthTokenServiceImpllVous pouvez vous référer au service de cache que vous utilisez ici, qui est également un résumé de mon expérience dans l'usine précédente.
4. Filtre d'enregistrement
Il existe deux façons courantes d'écrire:
(1) Utilisez l'annotation @webfilter pour identifier le filtre
@Order (1) @WebFilter (URLPatterns = {"/ api / v1 / biens / *", "/ api / v1 / userInfo / *"}) public classe authtokenfilter implémente le filtre {En utilisant l'annotation @webfilter, vous pouvez également utiliser @Order Annotation en conjonction avec l'annotation @Order. L'annotation @Order représente l'ordre de filtrage. Plus la valeur est petite, plus vous l'exécutez en premier. Cette taille de commande est aussi utile que le traitement du cycle de vie des demandes HTTP pendant notre processus de programmation. Bien sûr, si l'ordre n'est pas spécifié, l'ordre du filtre est appelé opposé à l'ordre des filtres ajoutés, et la mise en œuvre du filtre est le modèle de chaîne de responsabilité.
Enfin, ajoutez l'annotation @servletcomponentscan à la classe de démarrage pour utiliser normalement le filtre personnalisé.
(2) Utiliser le filterRecgressionBean pour personnaliser l'enregistrement du filtre
Cet article utilise la deuxième implémentation pour implémenter l'enregistrement des filtres personnalisés:
package com.power.demo.controller.filter; import com.google.common.collect.lists; import org.springframework.beans.factory.annotation.autowired; import org.springframework.boot.web.servlet.filterregmentbean; import org.springframeworkwork org.springframework.context.annotation.configuration; import org.springframework.sterereotype. @Bean Public FilterRegistrationBean FilterRegistrationBean () {FilterRegistrationBean RegistrationBean = new FilterRegrationBean (); RegistrationBean.SetFilter (filtre); // set (flou) correspondant à la liste URL <string> urlpatterns = lists.newArrayList (); urlPatterns.add ("/ api / v1 / biens / *"); urlpatterns.add ("/ api / v1 / userInfo / *"); RegistrationBean.SetUrlPatterns (URLPatterns); RegistrationBean.SetOrder (1); RegistrationBean.SetEnabled (true); Retour d'enregistrement BEAC; }} RestfilterconfigVeuillez prêter une attention particulière aux URLPatterns. L'URLPatterns d'attribut spécifie le modèle d'URL à filtrer. Ce paramètre est d'une grande importance pour la zone d'action filtrante.
Enregistrez le filtre et lorsque Spring Boot démarre, il ajoutera automatiquement la chaîne d'appels de filtre ApplicationFilterChain lorsqu'elle détecte un bean avec javax.servlet.filter.
Appelez une API pour essayer l'effet:
Habituellement, nous personnalisons une amélioration mondiale de la gestion des exceptions unifiées GlobalexceptionHandler sous Spring Boot (elle sera légèrement différente de ce qui précède).
Selon mon cabinet, les exceptions exercées dans le filtre ne seront pas capturées et traitées par l'amélioration de la gestion des exceptions globalement unique. Ceci est différent de l'intercepteur Intececeptor et de l'intercepteur AOP personnalisé introduit dans le prochain article.
À ce stade, un service d'authentification de sécurité simple implémenté via un filtre personnalisé est effectué.
3. Intercepteur personnalisé
1. Mettre en œuvre l'intercepteur
Hériter de l'interface HandlerInterceptor et implémentez l'intercepteur. Les méthodes d'interface sont les suivantes:
Prehandle est exécuté avant l'exécution de la demande
Postandle est la fin de l'exécution de la demande
Aftercompletion est exécuté une fois le rendu de la vue terminée
package com.power.demo.controller.interceptor; import com.power.demo.common.appconst; import com.power.demo.common.bizresult; import com.power.demo.service.contract.Authtokenservice. org.springframework.beans.factory.annotation.autowired; import org.springframework.sterreotype. javax.servlet.http.httpservletResponse; / * * Interceptor de jeton d'authentification * * / @ ComponentPublic classe AuthTokenInterceptor implémente HandlerInterceptor {@autowired private authenterService AuthToKenService; / * * Exécuter avant l'exécution de la demande * * / @Override public Boolean Prehandle (HttpServLetRequest Request, HttpServletResponse Response, Object Handler) lève l'exception {Boolean HandlerResult = false; String token = request.getheader (appcconst.auth_token); BizResult <string> bizResult = AuthTokenService.PowerCheck (token); System.out.println (serializeUtil.serialize (bizresult)); Handlerresult = bizresult.getSok (); Powerlogger.info ("Auth Token Interceptor Interceptor interceptor interceptor interceptor interceptor interceptor interceptor interceptor a réussi"); } else {lancer une nouvelle exception (bizresult.getMessage ()); } return handlerresult; } / * * La demande met fin à l'exécution * * / @Override public void Posthandle (HttpServLetRequest Request, httpservletResponse Response, Handler d'objet, ModelandView ModelAndView) lève une exception {} / * * Exécuter après le rendu de la vue est terminé * * / @Override public Void Aftercometion (HTTPServlate Handler, exception ex) lève l'exception {}} AuthTokenInterceptorDans l'exemple, nous choisissons d'effectuer l'authentification de sécurité des jetons avant l'exécution de la demande.
Le service d'authentification est le AuthTokenService introduit dans le filtre et la couche de logique métier est réutilisée.
2. Enregistrer l'intercepteur
Définissez une classe InterceptorConfig, héritée de WebMvcConfigurationsUpport, et le webmvcConfigurerAdapter est dépassé.
Injecter AuthToken Interceptor comme Bean, les URL et les filtres que les autres paramètres intercepteurs interceptent sont très similaires:
package com.power.demo.controller.interceptor; import com.google.common.collect.lists; import org.springframework.context.annotation.bean; import org.springframework.contex org.springframework.web.servlet.config.annotation.defaultServleThandlerConfigurer; import org.springframework.web.servlet.config.annotation.connotgistry; import org.springframework. org.springframework.web.servlet.config.annotation.webmvcconfigurationsupport; Importer java.util.list; @ configuration @ composantpublic class interceptorconfig webmvcconfigurationsupport {// webmvcconfigurerAdapter est tendu privé static static string favoncon_url = "/favicon.ico"; / ** * a constaté que si WebMvcConfigurationsUpport est hérité, le contenu pertinent configuré dans YML ne sera pas valide. * * @param registre * / @Override public void AddResourceHandlers (ResourceHandlerRegistry Registry) {registry.addreSourceHandler ("/"). AddResourceLocations ("/ **"); registry.addreSourceHandler ("/ static / **"). addResourceLocations ("classPath: / static /"); } / ** * Configurer le traitement du servlet * / @Override public void configuredEfaultServLeThandling (defaultServLethandLerConfigurer configure) {configurer.enable (); } @Override public void addInterceptors (Registre interceptorgistry) {// set (flou) correspondant à la liste URL <string> urlPatterns = lists.newArrayList (); urlPatterns.add ("/ api / v1 / biens / *"); urlpatterns.add ("/ api / v1 / userInfo / *"); registry.addinterceptor (authtokenterceptor ()). addPathPatterns (urlPatterns) .ExcludEPathPatterns (favicon_url); super.addinterceptors (registre); } // Écrivez l'intercepteur en tant que bean dans la configuration @bean public authtokenInterceptor authtokenInterceptor () {return new AuthTokenInterceptor (); }} InterceptorconfigAprès avoir commencé l'application, vous pouvez voir l'effet de l'interception intercepteur en appelant l'interface. Global Unified Excepte Gestion GlobalexceptionHandler gère les exceptions suivantes après les avoir attrapées:
C'est presque la même chose que le principal message d'erreur affiché par le filtre, mais les informations de pile sont plus riches.
4. La différence entre le filtre et l'intercepteur
Les principales différences sont les suivantes:
1. Les intercepteurs sont principalement basés sur le mécanisme de réflexion de Java, tandis que les filtres sont basés sur des rappels de fonction
2. L'intercepteur ne dépend pas du conteneur servlet, les filtres s'appuient sur le conteneur servlet
3. Les intercepteurs ne peuvent travailler que sur les demandes d'action, tandis que les filtres peuvent travailler sur presque toutes les demandes.
4. L'intercepteur peut accéder à l'objet dans le contexte d'action et la pile de valeur, mais le filtre ne peut pas y accéder.
5. Pendant le cycle de vie d'une action, l'intercepteur peut être appelé plusieurs fois, tandis que le filtre ne peut être appelé qu'une seule fois lorsque le conteneur est initialisé.
Certains articles que j'ai référencés ont déclaré que "l'intercepteur peut obtenir divers haricots dans le conteneur IOC, mais le filtre ne peut pas. Ceci est très important. L'injection d'un service dans l'intercepteur peut appeler la logique métier." Après une vérification réelle, c'est faux.
Remarque: Le temps de déclenchement du filtre est après le conteneur et avant le servlet, de sorte que le paramètre d'entrée du filtre Dofilter (demande de serviette, réponse servletResponse, chaîne de filterchain) est servletRequest, pas httpservletRequest, car le filtre est avant le Httpservlet. Le chiffre suivant peut vous donner une compréhension plus intuitive du calendrier d'exécution du filtre et de l'intercepteur:
Seules les demandes adoptées par DispatcherServlet seront suivies par la chaîne d'intercepteur. Les demandes de servlet personnalisées ne seront pas interceptées. Par exemple, notre adresse de servlet personnalisée http: // localhost: 9090 / testServlet ne sera pas interceptée par l'intercepteur. Mais peu importe à quel servlet il appartient, le filtre sera exécuté tant qu'il est conforme aux règles du filtre du filtre.
Selon l'analyse ci-dessus, la compréhension du principe sera simple, même les filtres ASP.NET seront les mêmes.
Problème: réaliser une authentification de sécurité plus flexible
Sous le Web Java, via le filtre de filtre personnalisé ou l'intercepteur intercepteur, une authentification sûre des API correspondantes spécifiques peut être réalisée, comme la correspondance de toutes les API, correspondant à une ou plusieurs API, etc., mais parfois ce modèle de correspondance n'est relativement pas convivial avec les développeurs.
Nous pouvons nous référer à Spring Security pour atteindre des fonctions puissantes via Annotation + Spel.
Par exemple, dans ASP.NET, nous utilisons souvent la fonctionnalité autorisée, qui peut être ajoutée aux classes ou appliquée aux méthodes, et pouvons contrôler l'authentification de la sécurité de manière plus dynamique et flexible.
Nous n'avons pas choisi Spring Security, nous pouvons donc mettre en œuvre une certification de sécurité flexible similaire à autorisé. La principale technologie de mise en œuvre est l'AOP que nous connaissons.
La connaissance de base de la réalisation d'une interception plus flexible grâce à la méthode AOP ne sera pas mentionnée dans cet article. D'autres sujets sur AOP seront partagés dans le prochain article.
Résumer
Ce qui précède est ce que l'éditeur vous a présenté. Spring Boot utilise des filtres et des intercepteurs pour obtenir une authentification simple et sécurisée des interfaces de repos. J'espère que ce sera utile à tout le monde. Si vous avez des questions, veuillez me laisser un message et l'éditeur répondra à tout le monde à temps. Merci beaucoup pour votre soutien au site Web Wulin.com!