Récemment, j'ai rencontré un problème dans le projet: le front-end et le back-end sont séparés, le front-end est effectué avec Vue, toutes les demandes de données utilisent Vue-Resource, et aucun formulaire n'est utilisé, de sorte que l'interaction des données est utilisée par JSON, l'arrière-plan utilise le démarrage de ressort et la vérification de l'autorisation n'utilise pas la sécurité printanière. Parce que Spring Security a été utilisé auparavant, ils ont traité des pages, et cette fois, ils ont simplement traité les demandes de l'Ajax, alors ils ont enregistré certains problèmes qu'ils ont rencontrés. La solution ici ne convient pas seulement aux demandes AJAX, mais résout également la vérification des demandes mobiles.
Créer un projet
Tout d'abord, nous devons créer un projet de démarrage de printemps. Lors de la création, nous devons introduire le Web, Spring Security, MySQL et MyBatis (le cadre de la base de données est en fait arbitraire, j'utilise MyBatis ici). Après la création, le fichier de dépendance est le suivant:
<dependency> <proupId> org.mybatis.spring.boot </prômId> <Artifactid> Mybatis-Spring-Boot-Starter </ ArfactId> <DERNÉE> 1.3.1 </ version> </peedendency> <dependency> <proupId> org.springframework.boot </proupId> <ArtefactId> Spring-Boot-Starter-Security </ ArfactId> </Dependency> <Dependency> <GroupId> org.SpringFramework.boot </proupId> <petifactid> printemps-boot-starter-web </retifactid> </dependency> <dependency> <proupId> mysql </proupId> <ArtefactId> MySQL-Connector-Java </Retifactid> <Cope> Runtime </cope> </Dependency> <Dependency> <ProupId> Commons-codec </rom grouped> <e Artifactid> Commons-codec </letefactive> <in version> 1.11 </-version> </Dedency>
Notez que la dernière dépendance des codes communs a été ajoutée manuellement par moi. Il s'agit d'un projet open source d'Apache qui peut être utilisé pour générer des digestions de message MD5. Je vais simplement traiter le mot de passe dans le texte suivant.
Créez une base de données et configurez-la
Afin de simplifier la logique, j'ai créé trois tables ici, à savoir la table utilisateur, le tableau des rôles et la table d'association des rôles utilisateur, comme suit:
Ensuite, nous devons effectuer une configuration simple de notre base de données dans Application.Properties. Ici, nous sommes déterminés par votre situation spécifique.
printemps.datasource.url = jdbc: mysql: ///vueblogspring.datasource.username=rootspring.datasource.password=123
Construire une classe d'entité
Ici, se réfère principalement à la construction de classes d'utilisateurs. Les cours d'utilisateur ici sont assez spéciaux et doivent implémenter l'interface UserDetails, comme suit:
La classe publique utilisateur implémente userDetails {private long id; Nom d'utilisateur de chaîne privée; mot de passe de chaîne privé; surnom de cordes privées; Activé de booléen privé; Rôles de liste privée <rôle>; @Override public boolean isAccountNonExpired () {return true; } @Override public boolean isAccountNonLocked () {return true; } @Override public boolean isCredentialSnOnexpired () {return true; } @Override public boolean iseNabled () {return activé; } @Override Public List <CessedAuthority> getAuthorities () {list <CessedAuthority> autorités = new ArrayList <> (); pour (rôle de rôle: rôles) {autorités.add (new SimpleGrantedAuthority ("role_" + role.getName ())); } Retour des autorités; } // Getter / Setter omettre ...} Après implémentation de l'interface UserDetails, il existe plusieurs méthodes dans cette interface que nous devons implémenter. Les quatre méthodes qui renvoient booléen sont toutes connues et connues. Activé indique si le compte période est activé. Ce champ existe dans ma base de données. Par conséquent, selon les résultats de la requête, l'autre revient directement pour les périodes simples. La méthode GetAuthorities renvoie les informations de rôle de l'utilisateur actuel. Le rôle de l'utilisateur est en fait les données dans les rôles. Les données dans les rôles sont converties en lister <CoredAuthority> puis retournées. Il y a un point à noter ici. Étant donné que les noms de rôles que je stockage dans la base de données sont tous tels que «super administrateur», «utilisateur ordinaire», etc., et ne commencez pas par des caractères comme ROLE_ , vous devez donc ajouter manuellement ROLE_ ici, n'oubliez pas.
Il existe également une classe d'entité de rôle, qui est relativement simple et peut être créée en fonction des champs de la base de données. Je ne le répéterai pas ici.
Créer un service d'utilisateur
Le service utilisateur ici est également assez spécial, et il est nécessaire d'implémenter l'interface UserDetailSservice, comme suit:
@ServicePublic Class UserService implémente userDetailSService {@Autowired UserMapper UserMapper; @Autowired Rolesmapper Rolesmapper; @Override public UserDetails LoadUserByUserName (String S) lève UserNamenotFoundException {user user = userMapper.LoadUserByUserName (s); if (user == null) {// éviter de retourner null, renvoie ici un objet utilisateur qui ne contient aucune valeur, et la vérification échouera également dans le processus de comparaison de mot de passe ultérieure renvoyer un nouvel utilisateur (); } // Interrogez les informations du rôle de l'utilisateur et retournez à l'utilisateur. List <role> rôles = rolemapper.getRolesByUID (user.getId ()); user.setRoles (rôles); RETOUR UTILISATEUR; }}Après implémentation de l'interface UserDetailSService, nous devons implémenter la méthode LoadUserByUserName dans l'interface, c'est-à-dire interroger l'utilisateur en fonction du nom d'utilisateur. Deux mappeurs dans MyBatis sont injectés ici, UserMapper est utilisé pour interroger les utilisateurs, et RolesMapper est utilisé pour interroger les rôles. Dans la méthode LoadUserByUserName, interrogez d'abord l'utilisateur en fonction des paramètres passés (le paramètre est le nom d'utilisateur entré lorsque l'utilisateur se connecte). Si l'utilisateur trouvé est NULL, vous pouvez directement lancer une exception UsernamenotFoundException. Cependant, pour la commodité du traitement, j'ai renvoyé un objet utilisateur sans aucune valeur. De cette façon, au cours du processus de comparaison des mots de passe ultérieure, vous constaterez que la connexion a échoué (ici, vous pouvez l'ajuster en fonction des besoins de votre entreprise). Si l'utilisateur trouvé n'est pas nul, nous interrogerons le rôle de l'utilisateur en fonction de l'ID utilisateur et mettrons le résultat de la requête dans l'objet utilisateur. Ce résultat de requête sera utilisé dans la méthode GetAuthorities de l'objet utilisateur.
Configuration de sécurité
Jetons un coup d'œil à ma configuration de sécurité d'abord, puis je l'expliquerai un par un:
@ConfigurationPublic Class WebSeCurityConfig étend WebSecurityConfigurerAdapter {@Autowired UserService UserService; @Override Protected void Configure (AuthenticationManagerBuilder Auth) lève une exception {auth.userDetailSService (userService) .PasswordEncoder (new passwatwayEncoder () {@Override public String Encode (CharSequence CharSequence) {return digestUtiles.md5Digestashex (CharSequence.Tostring (). * @Param CharSequence PlainText * @param s CipherText * @return * / @Override public Boolean Matches (CharSequence CharSequence, String S) {return s.equals (digestUtils.md5digestashex (CharSequence.Tostring (). Getbytes ()))); } @Override Protected void configure (httpSecurity http) lève exception {http.authorizequests () .antmatchers ("/ admin / **"). Hasrole ("super admin") .AnyRequest (). in.and (). FormLogin (). Loginpage ("/ login_page"). SuccessHandler (New AuthenticationsUCSHandler () {@Override public void onAuthenticationsUCCESS (httpservletRequest httpservletrequest, HttServletResponse HttpleTReRsPonse, authentification Authentification) Servlexception {httpServletResponse.setContentType ("Application / JSON; CHARSED = UTF-8"); }) .failureHandler (new AuthenticationFailureHandler () {@Override public void onAuthenticationFailure (httpservletRequest httpservletRequest, httpservletResponse httpservletResponse, AuthenticationException e) lance ioexception, servlexect { httpServletResponse.setContentType ("Application / JSON; charmet = utf-8"); }). LoginProcessingUrl ("/ Login") .UserAmAramètre ("nom d'utilisateur"). PasswordParameter ("Password"). PermitAll () .and (). Logout (). PermitAll (). et (). Csrf (). Disable (); } @Override public void configure (WebSecurity web) lève une exception {web.ignoring (). Antmatchers ("/ reg"); }}C'est le cœur de notre configuration. S'il vous plaît écoutez-moi:
1. Tout d'abord, il s'agit d'une classe de configuration, alors n'oubliez pas d'ajouter l'annotation @configuration. Comme il s'agit de la configuration de Spring Security, vous devez hériter du WebSecurityConfigurerAdapter.
2. Injecter le service d'utilisateur qui vient d'être créé et nous l'utiliserons plus tard.
3.Configure (AuthenticationManagerBuIlder Auth) La méthode est utilisée pour configurer notre méthode d'authentification et passer la méthode utilisateur dans la méthode Auth.UserDeTailSService (), de sorte que la méthode LoadUserByUserName dans le service utilisateur sera automatiquement appelée lorsque l'utilisateur se connecte. Un mot de passe en clair est également nécessaire pour le traiter lors de la connexion, donc j'ai ajouté le mot de passe-code, et après avoir ajouté un mot de passe, je peux directement nouveau une classe interne anonyme de mot de passe-mot de passe. Il existe deux méthodes à mettre en œuvre ici, et vous pouvez connaître la signification de la méthode en regardant le nom. La première méthode Encode chiffre évidemment le texte en clair. Ici, j'utilise le MD5 Message Digest. La méthode de mise en œuvre spécifique est fournie par Commons-Codec Dependency; Les matchs de la deuxième méthode sont la comparaison du mot de passe, deux paramètres, le premier paramètre est le mot de passe en texte clair et le second est CipherText. Ici, il vous suffit de crypter le texte en clair et de le comparer avec le texte chiffré (si vous vous y intéressez, vous pouvez continuer à envisager d'ajouter du sel au mot de passe).
4.Configure (httpSecurity http) est utilisé pour configurer nos règles d'authentification, etc. La méthode AutorizeRequests signifie que la configuration de la règle d'authentification est activée, Antmatchers ("/ admin / **"). Hasrole ("Super Administrator") signifie que le chemin de /admin/** doit être accessible par les utilisateurs avec le rôle "Super Administrator '. J'ai vu sur Internet que mes amis ont des questions sur l'opportunité d'ajouter ROLE_ dans la méthode Hasrole. Ne l'ajoutez pas ici. Si vous utilisez la méthode Hasauthority, vous devez l'ajouter. anyRequest (). Authenticated () signifie que tous les autres chemins doivent être authentifiés / connectés avant l'accès. Ensuite, nous avons configuré la page de connexion en tant que login_page, le chemin de connexion de connexion est / connexion, le nom d'utilisateur de connexion est le nom d'utilisateur et le mot de passe est le mot de passe. Nous avons configuré ces chemins pour accéder directement, et nous nous connectons à la connexion peut également être accessible directement, et enfin fermer le CSRF. Dans SuccessHandler, utilisez la réponse pour renvoyer le JSON qui s'est connecté avec succès. N'oubliez pas de ne pas utiliser defaultSuccessurl. DefaultSuCcessUrl est une page qui est redirigée uniquement après la connexion avec succès. La même raison est également utilisée pour l'échec de Handandler.
5. J'ai configuré certaines règles de filtrage dans la méthode de configuration (WebSecurity Web), donc je ne vais pas entrer dans les détails.
6. De plus, pour les fichiers statiques, tels que /images/** , /css/** , et /js/** , la valeur par défaut n'est pas interceptée.
Contrôleur
Enfin, jetons un coup d'œil à notre contrôleur, comme suit:
@RestControllerPublic Class LoginRegController {/ ** * Si vous passez automatiquement à cette page, cela signifie que l'utilisateur n'est pas connecté, et vous pouvez retourner l'invite correspondante * <p> * Si vous souhaitez prendre en charge la connexion du formulaire, vous pouvez juger le type de demande dans cette méthode, puis décider de retourner à JSON ou html * * @return * / @requestmapping (" Respbean loginpage () {return new respbean ("error", "non connecté encore, veuillez vous connecter!"); }} Dans l'ensemble, ce contrôleur est relativement simple. Respbean renvoie un simple JSON, qui n'est pas détaillé. Ce à quoi vous devez faire attention ici, c'est login_page . La page de connexion que nous avons configurée est un login_page . Mais en fait, login_page n'est pas une page, mais un JSON. En effet, lorsque je visite d'autres pages sans connexion, Spring Security sautera automatiquement à la page login_page . Cependant, dans la demande d'Ajax, ce type de saut n'est pas nécessaire. Tout ce que je veux, c'est une invite pour me connecter ou non, donc je peux retourner JSON ici.
test
Enfin, les amis peuvent utiliser des outils tels que Postman ou RestClient pour tester les problèmes de connexion et d'autorisation, donc je ne le démontrerai pas.
OK, après l'introduction ci-dessus, je crois que les amis ont déjà compris le traitement de Spring Boot + Spring Security des demandes de connexion AJAX. D'accord, c'est tout pour cet article. Si vous avez des questions, veuillez laisser un message à discuter.