Aperçu
Comme nous le savons tous, l'utilisation de JWT pour la vérification de l'autorisation. Par rapport à la session, l'avantage de la session est que la session nécessite une grande quantité de mémoire de serveur, et lorsque plusieurs serveurs sont utilisés, il impliquera des problèmes de session partagés, ce qui est plus gênant lors de l'accès aux terminaux mobiles tels que les téléphones mobiles.
JWT n'a pas besoin d'être stocké sur le serveur et n'occupe pas les ressources du serveur (c'est-à-dire sans état). Une fois que l'utilisateur s'est connecté, il se fixe au jeton lors de l'accès à la demande qui nécessite une autorisation (généralement définie dans l'en-tête de demande HTTP). JWT n'a pas le problème de partager plusieurs serveurs, et il n'a pas non plus de problèmes d'accès mobile sur les téléphones mobiles. Si pour améliorer la sécurité, le jeton peut être lié à l'adresse IP de l'utilisateur
Processus frontal
L'utilisateur s'est connecté via Ajax pour obtenir un jeton
Après cela, lorsque l'accès nécessite une autorisation, joignez un jeton pour accéder.
<! Doctype html> <html lang = "en"> <éad> <meta charset = "utf-8"> <title> title </ title> <script src = "http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"> </cript> <script type = "application / javascript"> var = "; fonction login () {$ .post ("http: // localhost: 8080 / auth / login", {username: $ ("# username"). Val (), mot de passe: $ ("# mot de passe"). Val ()}, fonction (données) {console.log (data); en-tête =})} function touserpagebt () {$. "Get", URL: "http: // localhost: 8080 / userpage", beForessend: function (request) {request.setRequestHeader ("Autorisation", en-tête);}, Success: Function (Data) {Console.Log (Data); } </ script> </ head> <body> <fieldset> <gend> Veuillez vous connecter </GENEND> <QUALD> Nom d'utilisateur </Bel> <entrée type = "Text" id = "username"> <label> mot de passe </ label> <entrée type = "Text" id = "mot de passe"> <entrée type = "Button" OnClick = "Login ()" Value = "Login"> </ fieldSet> id = "TauserPageBtn" onClick = "TauserPageBtn ()"> Accédez à UserPage </ftont> </odody> </html>Processus backend (Spring Boot + Spring Security + JJWT)
Idées:
Écrivez une classe d'entité utilisateur et insérez un morceau de données
Classe d'entité utilisateur (utilisateur)
@ Data @ EntityPublic classe utilisateur {@id @GeneratedValue private int id; nom de chaîne privé; mot de passe de chaîne privé; @Manytomany (cascade = {cascadetype.refresh}, fetch = fetchtype.eager) @jointable (name = "user_role", joinculumns = {@joincumn (name = "uid", référencécolumnname = "id")}, inversejoincumns = {@joincolumn (name = "rid", ring référencéColumnName = "id")}) Liste privée <rôles des rôles;} Classe d'entité de rôle (autorisation)
@ Data @ EntityPublic Class Role {@id @GeneratedValue private int id; nom de chaîne privé; @ManyTomany (mappedBy = "rôles") Liste privée <servy> utilisateurs;} Insérer des données
Table des utilisateurs
| identifiant | nom | mot de passe |
|---|---|---|
| 1 | linyuan | 123 |
Table de rôle
| identifiant | nom |
|---|---|
| 1 | UTILISATEUR |
Tableau user_role
| uid | débarrasser |
|---|---|
| 1 | 1 |
Interface de couche DAO, obtient des données via le nom d'utilisateur et renvoie un objet facultatif avec une valeur de Java8
Interface publique UserRepository étend le référentiel <utilisateur, entier> {facultatif <server> findByName (nom de chaîne);} Écrivez Logindto pour le transfert de données avec le frontal
@Datapublic class LogindTo implémente serializable {@notblank (message = "le nom d'utilisateur ne peut pas être vide") Nom d'utilisateur de chaîne privée; @Notblank (message = "le mot de passe ne peut pas être vide") Mot de passe de chaîne privée;} Écrivez un outil de génération de jetons et créez-le à l'aide de la bibliothèque JJWT. Il y a trois méthodes au total: générer un jeton (retourner une chaîne), analyser le jeton (retourner un objet d'authentification d'authentification) et vérifier le jeton (renvoyer une valeur booléenne)
@Componentpublic class jwttokenutils {private final logger log = loggerfactory.getlogger (jwttokenutils.class); Private Static Final String autorities_key = "Auth"; SCREAT PRIVÉ SECRETKEY; // Signer la clé de la clé privée longue de laValidité en fin de modification; // Date d'expiration privée Long TokenvalidityInmilLisecondsForrememberme; // (Remember Me) Expiration Date @postConstruct public void init () {this.secretkey = "LinyuanMima"; int secondIn1day = 1000 * 60 * 60 * 24; this.tokenvalidityInMilliseconds = SecondIn1day * 2l; this.tokenvalidityInmilLiseConDSForrembemberMe = SecondIn1day * 7l; } private final statique long expirationtime = 432_000_000; // Créer des chaînes publiques de jetons CreateToken (authentification Authentication, boolean reemmMe) {String autorités = authentication.getAuthorities (). Stream () // Obtenez la chaîne d'autorisation de l'utilisateur, tel que l'utilisateur, admin .map (GredgedAuthority :: GetAuthority) .Collect. long maintenant = (new Date ()). getTime (); // obtient la validité actuelle de la date de l'horodatage; // Temps d'expiration de stockage if (reemmMe) {validité = new Date (maintenant + this.tokenvalidityInMilliseconds); } else {validity = new Date (maintenant + this.tokenvalidityInmilLiseCondsForrememberMe); } return jwts.builder () // Créer un token token.setsubject (authentication.getName ()) // set user-oriented.claim (autorités_key, autorités) // Add Permission Attribut.SetExpiration (validité) // invalidation time.signwith (signaturalgorithm.hs512, SecretKey) // générer la signature ();); } // Obtenez des autorisations utilisateur Authentification publique GetAuthentication (String Token) {System.out.println ("Token:" + Token); Réclame des réclamations = jwts.parser () // Parse Token's Upload .SetsigningKey (SecretKey) .PaSeclaidsJws (token) .getBody (); Collection <? étend conçu Authority> autorités = arrays.stream (revendingS.get (autorités_key) .toString (). Split (",")) // Get User Permission String.map (SimpleGrantedAuthority :: new) .Collect (collecors.tolist ()); // Convertir les éléments en collection d'interface d'autorité accorde principale utilisateur = nouvel utilisateur (revendiqué.getSubject (), "", autorités); Renvoie un nouveau userNamepasswordAuthenticationToken (directeur, "", autorités); } // Vérifiez si le jeton est correct public booléen validateToken (token de chaîne) {try {jwts.parser (). SetSigningKey (SecretKey) .ParseclaidsJws (token); // Vérifiez le jeton par clé Retour True; } catch (SignatureException e) {// Exception de signature log.info ("signature JWT invalide."); log.trace ("Trace de signature JWT invalide: {}", e); } catch (malformedjwtexception e) {// jwt format error log.info ("token jwt invalid."); Log.Trace ("Trace de jeton JWT non valide: {}", e); } catch (expiredjwtexception e) {// jwt Expire Log.info ("Token jwt expiré."); Log.Trace ("Token JWP expiré Trace: {}", E); } catch (Unpattedjwtexception e) {// le jwt log.info ("token jwt non pris en charge."); Log.Trace ("Trace de jeton JWT non pris en charge: {}", e); } catch (illégalArgumentException e) {// L'exception d'erreur de paramètre log.info ("le compact token jwt du gestionnaire n'est pas valide."); Log.Trace ("JWT Token Compact of Handler est invalide Trace: {}", E); } return false; }} Implémentation de l'interface UserDetails, représentant la classe d'entité utilisateur, est enveloppée sur notre objet utilisateur, contient des autorisations et d'autres propriétés et peut être utilisée par Spring Security
classe publique MyUserDetails implémente UserDetails {utilisateur privé User; public myuserDetails (utilisateur utilisateur) {this.user = utilisateur; } @Override public Collection <? étend conçue Authority> getAuthorities () {list <role> rôles = user.getRoles (); List <GrevendAuthority> autorités = new ArrayList <> (); StringBuilder sb = new StringBuilder (); if (rôles.size ()> = 1) {pour (rôle de rôle: rôles) {autorités.add (new SimpleGrantedAuthority (role.getName ())); } Retour des autorités; } Retour des autorités; } RETOUR AUTORITALUSUTILS.COMMASEPARATEDSTRINGTOAUTHORITYLIST (""); } @Override public String getPassword () {return user.getPassword (); } @Override public String getUserName () {return user.getName (); } @Override public boolean isAccountNonExpired () {return true; } @Override public boolean isAccountNonLocked () {return true; } @Override public boolean isCredentialSnOnexpired () {return true; } @Override public boolean iseNabled () {return true; }} Implémentez l'interface UserDetailSService, qui n'a qu'une seule méthode pour obtenir UserDetails. Nous pouvons obtenir l'objet utilisateur à partir de la base de données, puis l'envelopper dans UserDetails et le retourner
@ServicePublic class MyUserDetailSService implémente userDetailSService {@autowired userRepository userRepository; @Override public UserDetails LoadUserByUserName (String S) lance UserNamenotFoundException {// Chargez un objet utilisateur à partir de la base de données facultative <serv> user = userRepository.FindByName (s); // Pour le débogage, si la valeur existe, le nom d'utilisateur et le mot de passe sont sortis. Ifpresent ((valeur) -> System.out.println ("username:" + value.getName () + "Mot de passe utilisateur:" + value.getPassword ())); // Si la valeur n'est plus, renvoyez null return new MyUserDetails (user.orelse (null)); }} Écrivez un filtre. Si l'utilisateur transporte le jeton, il obtiendra le jeton et générera un objet d'authentification d'authentification basé sur le jeton et le stockera dans la sécurité de sécurité pour le contrôle d'autorisation par Spring Security.
La classe publique jwtAuthenticationTokenFilter étend GenericFilterBean {private final logger log = loggerfactory.getLogger (jwtAuthenticationTokenFilter.class); @Autowired Private Jwttokenutils tokenprovider; @Override public void dofilter (ServletRequest ServletRequest, servletResponse ServletResponse, filterchain filterChain) lance ioException, servletException {System.out.println ("jwtAuthenticationTokenFilter"); essayez {httpServletRequest httpreq = (httpServLetRequest) servLetRequest; String JWT = RESOLVETOKIN (Httpreq); if (stringUtils.hastext (jwt) && this.tokenprovider.validateToken (jwt)) {// Vérifiez si le jwt est une authentification correcte d'authentification = this.tokenprovider.getAuthentication (JWT); // Obtenez des informations d'authentification utilisateur SecurityContexTholder.getContext (). SetAuthentication (authentification); // Enregistrez l'utilisateur sur SecurityContext} filterchain.dofilter (servletRequest, servletResponse); } catch (expiredjwtexception e) {// jwt invalid log.info ("exception de sécurité pour l'utilisateur {} - {}", e.getClaims (). getSubject (), e.getMessage ()); Log.Trace ("Security Exception Trace: {}", E); ((HttpServletResponse) ServletResponse) .SetStatus (httpServletResponse.sc_unAuthorized); }} private String ResolveToken (HttpServleRequest Request) {String Bearertoken = request.GetHeader (WebSecurityConfig.authorization_header); // Get Token de l'en-tête http if (StringUtils.hastext (Bearertoken) && Bearertoken.startswith ("Bearer")) {return Bekeertoken.Substring (7, BearertKeN.Length ()); // Renvoie la chaîne de token et supprimer le porteur} chaîne jwt = request.getParameter (WebSecurityConfig.authorization_token); // Get token à partir des paramètres de demande if (stringUtils.hastext (jwt)) {return jwt; } return null; }} Écrivez un LoginController. L'utilisateur accède / Auth / Connexion via le nom d'utilisateur et le mot de passe, le reçoit via l'objet LogindTo et crée un objet d'authentification. Le code est userNamepasswordAuthenticationToken pour déterminer si l'objet existe. Vérifiez l'objet d'authentification via la méthode d'authentification d'authentification. Le fournisseur d'implémentation AuthenticationManager ProviderManager vérifiera par l'authentificationProvider (traitement d'authentification). Le ProviderManager par défaut appelle DaoAuthenticationProvider pour le traitement de l'authentification. Le DaoAuthenticationProvider obtiendra UserDetails via UserDetailSService (source d'informations d'authentification), si l'authentification est réussie, une autorité contenant des autorisations est renvoyée, puis la définir sur SecurityContext via SecurityContexTholder.getContext (). SetAuthentication (), générer un jeton selon l'authentification, et les rendre à l'utilisateur.
@RestControllerPublic Class LoginController {@Autowired Private UserRepository UserRepository; @Autowired Private AuthenticationManager AuthenticationManager; @Autowired Private Jwttokenutils jwttokenutils; @RequestMapping (value = "/ auth / login", méthode = requestMethod.post) public String Login (@valid logindto logindto, httpservletResponse httpResponse) lève une exception {// créer une authentification d'authentification par le nom d'utilisateur et le mot de passe, et implémentez la classe en tant qu'UserNamePasswordAnticationTokenTokre Nouveau userNamePasswordAuthenticationToken (logindto.getUserName (), logindto.getPassword ()); // si l'objet d'authentification n'est pas vide if (objets.nonnull (authenticationToken)) {userRepository.FindByName (authenticationToken.getPrincipal (). ToString ()) .OrelSethrow (() -> Nouvelle exception ("l'utilisateur n'existe pas")); } essayez {// Vérifiez l'objet d'authentification via AuthenticationManager (par défaut implémenté comme ProviderManager) Authentication = AuthenticationManager.AuthentiCate (AuthenticationToken); // lier l'authentification à SecurityContext SecurityContexTholder.getContext (). SetAuthentication (authentification); // Générer le jeton de token token = jwttokenutils.createToken (authentification, false); // Écrivez le jeton dans l'en-tête HTTP httpResponse.addheader (WebSecurityConfig.authorization_header, "Bearer" + Token); retourner "porteur" + jeton; } catch (BadCredentialSException Authentication) {Throw New Exception ("Mot de passe Erreur"); }}} Écrivez la classe de configuration de sécurité, héritez du WebSecurityConfigurerAdapter et remplacez la méthode de configuration
@ Configuration @ activerwebsecurity @ activeglobalthodsecurity (pressenabled = true) public class webseCurityConfig étend WebSecurityConfigurerAdapter {public static final string Authorization_header = "Authorization"; public static final String Authorization_token = "Access_token"; @Autowired private userDetailSService UserDetailSService; @Override Protected void Configure (AuthenticationManagerBuilder Auth) lève une exception {Auth // Personnaliser pour obtenir des informations utilisateur.USERDETAILSSERVICE (UserDetailSService) // Définissez le mot de passe Encryption.PassWordOner (PasswordEncoder ()); } @Override Protected void configure (httpsecurity http) lève une exception {// Configurer la politique d'accès à la demande http // close csrf et cors .Cors (). Disable () .csrf (). Disable () // sécession n'est pas requise puisque jet .and () // Vérifiez http request.AuthorizeRequests () // permettre à tous les utilisateurs d'accéder à la page d'accueil et de se connecter. .and () // set logout (). permutall (); // Ajouter un filtre JWT sur HTTP .AddFilterBefore (genericFilterBean (), userNamePasswordAuthenticationFilter.class); } @Bean Public PasswordEncoder PasswordEncoder () {return new BCryptPasswordEncoder (); } @Bean public genericfilterbean genericfilterbean genericfilterbean () {return new JWtAuthenticationTokenFilter (); }} Écrivez un contrôleur pour tester
@RestControllerPublic class userController {@postMapping ("/ Login") public String Login () {return "Login"; } @GetMapping ("/") public String index () {return "Hello"; } @Getmapping ("/ userpage") String public httpapi () {System.out.println (SecurityContexTholder.getContext (). GetAuthentication (). GetPrincipal ()); retourner "userpage"; } @Getmapping ("/ adminpage") String public httpsuite () {return "userpage"; }}Téléchargement du code source de cas (téléchargement local)
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.