SpringBoot + Shiro réalise l'authentification de connexion (processus d'implémentation + analyse du code source + affichage du code)

1. Vue d'ensemble

1.1 SpringBoot

Ce que nous devons faire aujourd'hui est d'utiliser SpringBoot pour coopérer avec Shiro pour obtenir l'authentification de connexion, donc SpringBoot est indispensable. Je pense que tout le monde peut utiliser Shiro, et SpringBoot ne doit pas être mauvais, donc je n'entrerai pas trop dans les détails, nous allons principalement présenter Shiro.

1.2 Shiro

Shiro est un framework de sécurité Java. Il est puissant et facile à utiliser. Comparé à Spring Security, il est plus largement utilisé. Shiro a de grands avantages en termes de simplicité et de flexibilité tout en conservant des fonctions puissantes.
Insérez la description de l'image ici
Les fonctions de Shiro incluent également les éléments suivants:

  • Authentification: authentification d'identité, pour vérifier si un utilisateur possède une identité.
  • Autorisation: vérification des autorisations, pour vérifier si un utilisateur authentifié dispose d'une certaine autorisation. Déterminez «qui» peut accéder à «quoi».
  • Gestion de session: gestion de session, gérer la session après la connexion de l'utilisateur,
  • Cryptographie: cryptage, utilisez la cryptographie pour crypter les données, telles que les mots de passe cryptés.
  • Support Web: le support Web peut être facilement intégré à l'environnement Web.
  • Mise en cache: mise en cache, mise en cache des données utilisateur,
  • Concurrence: Concurrence, Apache Shiro prend en charge les applications multithreads avec des fonctions simultanées, ce qui signifie qu'il prend en charge la vérification simultanée dans les applications multithreads.
  • Testing: Testing, qui fournit un support de test.
  • Exécuter en tant que: autorise les utilisateurs à se connecter en tant qu'autres utilisateurs.
  • Souvenez-vous de moi: souvenez-vous de moi,
    parmi lesquels la vérification des autorisations d'authentification et d'autorisation est préférable de manger ensemble!
    Aujourd'hui, nous parlons principalement de la façon de réaliser l'authentification d'identité

2. Shiro réalise l'authentification de connexion

Avant d'utiliser Shiro pour l'authentification de connexion, nous devons d'abord comprendre quelques problèmes conceptuels. Premièrement, comme excellent cadre de sécurité, Shiro doit avoir un centre de gestion central, qui est la classe DefaultWebSecurityManager , que nous personnaliserons plus tard Tous les objets de méthode seront ajoutés à ce centre de gestion, qui contrôlera les méthodes que nous définissons pour remplacer la méthode d'authentification par défaut fournie avec le système. Deuxièmement, Realm est également un objet très important, traduit par «domaine», qui agit comme un «pont» ou un «connecteur» entre Shiro et les données de sécurité des applications. La méthode que nous personnalisons doit être écrite dans une classe qui hérite de AuthorizingRealm avant de pouvoir être ajoutée au centre de gestion. Le dernier concept est Sujet . Habituellement, nous comprenons l'objet Sujet comme un utilisateur. De même, il peut aussi s'agir d'un programme à trois parties. C'est un concept abstrait et peut être compris comme toute «chose» qui interagit avec le système est un Sujet. Nous l'utiliserons pour nous connecter à l'authentification.

Importer les dépendances pom

Créez d'abord un projet SpringBoot et importez les dépendances shiro et springboot pom.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
                <!--shiro-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

Après la configuration, nous allons définir le fichier de configuration application.yml, et définir notre numéro de port springboot à l'intérieur.

server:
  port: 8080  #自定义

Ensuite, configurez notre classe de démarrage SpringBoot.

@SpringBootApplication
public class ShiroApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(ShiroApplication.class);
    }
}

Configurer la méthode principale

Après cela, nous configurons une méthode Controller pour s'interfacer avec la page, nous transmettons le mot de passe du compte pour tester la situation de connexion et testons les autorisations avant et après la connexion.

package com.df.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;


@RestController
public class LoginController {
    
    
    //如果需要使用shiro长期登陆,设置subject的rememberMe属性并且设置允许的范围为user。authc不允许被rememberMe用户访问。
    //这就是我们传入账号密码测试的地方
    @PostMapping(value = "/doLogin")
    public void doLogin(@RequestParam(value = "username") String username,
                        @RequestParam(value = "password") String password){
    
    
        Subject subject = SecurityUtils.getSubject();
        try {
    
    
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
            subject.login(usernamePasswordToken);

            System.out.println("登陆成功");
        }catch (Exception e){
    
    
            e.printStackTrace();
            System.out.println("登陆失败");
        }
    }
	
    @RequestMapping(value = "/index")
    public String index(){
    
    
        System.out.println("欢迎来到主页");
        return "欢迎来到主页";
    }
    
    //我们可以使用postman进行调用测试 登录前后hello的区别
    @GetMapping(value = "/hello")
    public String hello(HttpServletRequest request){
    
    
        Cookie[] cookies = request.getCookies();
        System.out.println(cookies[0].getValue());
        return "hello";
    }
	//用来设置未登录用户跳转的方法
    @GetMapping(value = "/login")
    public String login(){
    
    
        return "Please Login !";
    }
	//注销方法
    @GetMapping(value = "/logout")
    public String logout(){
    
    
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        System.out.println("成功退出");
        return "success to logout";
    }
}

Après avoir configuré la méthode Controller, nous allons créer un royaume pour configurer l'authentification du compte de connexion. Créez un fichier de configuration et définissez une classe MyRealm pour hériter de AuthorizingRealm .
Insérez la description de l'image ici
Ensuite, nous mettrons en œuvre le processus d'authentification. Après l'héritage, nous devons implémenter deux méthodes. L'une est la méthode d'authentification et l'autre est la méthode d'autorisation. Il suffit de compléter la méthode d'authentification dans cette section.

package com.df.config;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * @author Lin
 * @create 2020/7/15
 * @since 1.0.0
 * (功能):
 */
public class MyRealm extends AuthorizingRealm {
    
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
    
        return null;
    }

    @Override //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
    
       //从被shiro封装成的token中取出我们传入的username
        String username = (String) authenticationToken.getPrincipal();
		//这里应有一步去缓存或数据库查询的步骤,我省略了
        //我直接定义了一个username,如果用户名不匹配,则报错用户名不存在。
        if(!"LinJy".equals(username)){
    
    
            throw new UnknownAccountException("账号不存在");
        }
        //返回一个新封装的认证实体,传入的是用户名,数据库查出来的密码,和当前Realm的名字
        return new SimpleAuthenticationInfo(username, "123", this.getName());
    }
}

Dans ce qui précède, nous avons terminé le processus d'authentification du compte. Il doit y avoir beaucoup d'amis qui demandent, après l'authentification du compte, qu'en est-il du mot de passe? Pourquoi le mot de passe n'est-il pas authentifié ensemble? Commençons par écrire le mot de passe d’authentification. Shiro divise le compte et le mot de passe en deux endroits pour vérification. Si nous ne le définissons pas nous-mêmes, la méthode de vérification par défaut de shiro sera appelée.
Nous recréons une classe MyCredentialsMatcher héritant de SimpleCredentialsMatcher pour implémenter notre méthode de vérification de mot de passe personnalisée.

package com.df.config;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;


public class MyCredentialsMatcher extends SimpleCredentialsMatcher {
    
    
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    
    
        UsernamePasswordToken tokenResolve = (UsernamePasswordToken) token;
        String tokenPwd = new String(tokenResolve.getPassword());
        String infoPwd =(String) info.getCredentials();
        //调用当前类重写的equals方法来对比两个password是否一致,返回对比结果
        return super.equals(tokenPwd, infoPwd);
    }
}

Après avoir configuré le compte et le mot de passe, nous devons configurer un gestionnaire pour rapprocher nos composants de vérification de compte et de mot de passe de shiro. C'est encore une fois notre classe de configuration qui sort. La fonction principale de la classe de configuration est d'assembler certains des accessoires que nous avons fabriqués auparavant et de configurer un intercepteur pour intercepter les demandes de connexion dont nous avons besoin pour nous connecter. Pas grand chose à dire directement sur le code, il y a des commentaires spécifiques dans le code.

package com.df.config;


import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    
    
	//引入之前定义好的域
    @Bean
    MyRealm myRealm(){
    
    
        return new MyRealm();
    }
	//配置一个安全管理器
    @Bean
    DefaultWebSecurityManager securityManager(){
    
    
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        MyRealm myRealm = myRealm();
        //将我们配置好的密码校验放入域中
        myRealm.setCredentialsMatcher(myCredentialsMatcher());
        //将域添加到我们的安全管理器中
        manager.setRealm(myRealm);
        //设置Session管理器,配置shiro中Session的持续时间
        manager.setSessionManager(getDefaultWebSessionManager());

        return manager;
    }
	//引入密码校验
    @Bean
    public MyCredentialsMatcher myCredentialsMatcher(){
    
    
        return new MyCredentialsMatcher();
    }

    //设置session过期时间
    @Bean
    public DefaultWebSessionManager getDefaultWebSessionManager() {
    
    
        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        defaultWebSessionManager.setGlobalSessionTimeout(1000 * 60);// 会话过期时间,单位:毫秒--->一分钟,用于测试
        defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
        defaultWebSessionManager.setSessionIdCookieEnabled(true);
        return defaultWebSessionManager;
    }



	//设置访问拦截器
    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean(){
    
    
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //传入安全管理器
        bean.setSecurityManager(securityManager());
        //传入未登录用户访问登陆用户的权限所跳转的页面
        bean.setLoginUrl("/login");

        //设置成功后返回页面
        bean.setSuccessUrl("/index");

        //访问未授权网页所跳转的页面
        bean.setUnauthorizedUrl("/unauthorized");
        Map<String, String> map = new LinkedHashMap<>();
        //允许  需要设置login为anon 否则登陆成功后无法成功跳转。
        map.put("/login", "anon");
        map.put("/doLogin", "anon");
        map.put("/index", "anon");
        //设置所有的请求未登录不允许进入。
        map.put("/**", "authc");
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }

}

Ensuite, nous pouvons démarrer SpringBoot pour un essai. Il est recommandé d'accéder d'abord à la requête interceptée telle que: / hello, et vous constaterez qu'elle n'est pas accessible, puis connectez-vous: / doLogin, puis accédez à hello. Je posterai le code source sur github, le lien d'adresse est en bas et une copie de dubbo + zookeeper integrated shiro est jointe.

3. Analyse du code source d'authentification de connexion Shiro

Ensuite, voyons comment shiro implémente la vérification du compte et du mot de passe étape par étape. Tout d'abord, nous devrions commencer par la méthode familière / doLogin.
Insérez la description de l'image ici
Après être entré, nous avons constaté que shiro avait donné le jeton que nous avions entré au responsable de la sécurité pour l'appel
Insérez la description de l'image ici
. Après être entré dans le gestionnaire de sécurité, nous avons constaté que le responsable de la sécurité avait appelé une méthode de connexion, transmis notre jeton et entré cette méthode pour créer le compte Realm Certification.
Insérez la description de l'image ici
Ce qui précède est de juger si le jeton que nous avons transmis est problématique, sinon, entrez le sujet et appelez la méthode d'authentification. À partir du rapport d'erreur ci-dessous, nous pouvons comprendre qu'il jugera de notre besoin d'authentifier le compte.
Insérez la description de l'image ici
Alors venez ici, la première étape de this.assertRealmConfigured () est un jugement sur Realm, qui juge si Realm est une valeur nulle, puis obtient Realm. Parce que nous avons déjà configuré un royaume, nous appellerons le seul royaume, sinon nous traverserons le royaume, puis appellerons le jugement un par un. Voici le code pour appeler plusieurs royaumes.
Insérez la description de l'image ici
Si nous allons plus loin, nous pouvons voir qu'il juge d'abord si le royaume prend en charge le jeton, et s'il le fait, il appelle le royaume pour l'authentification.
Insérez la description de l'image ici
Entrez ensuite dans le royaume pour obtenir le cache précédemment connecté, s'il est vide, le processus d'authentification commencera,
Insérez la description de l'image ici
oups! Il peut être considéré comme une méthode que nous connaissons, c'est la méthode que nous avons définie auparavant pour juger le nom d'utilisateur. Une fois le jugement réussi, le nom d'utilisateur et le mot de passe trouvés dans la base de données sont reconditionnés. Puis revenez.
Insérez la description de l'image ici
Une fois l'authentification du compte effectuée, les informations sont renvoyées, visibles sur l'image. Si les informations ne sont pas nulles, le mot de passe sera vérifié.
Insérez la description de l'image ici
Après avoir entré, nous avons d'abord obtenu un objet CredentialsMatcher. Pour cet objet, nous avons trouvé sa classe d'implémentation d'interface et avons constaté que nous avons implémenté la méthode de correspondance de mot de passe, nous allons donc appeler notre objet personnalisé. Bien sûr, si nous ne personnalisons pas, le mot de passe de cryptage par défaut sera utilisé pour comparer l'authentification.
Insérez la description de l'image ici
Insérez la description de l'image ici
Ensuite, nous continuons vers le bas et entrons dans l'étape de l'authentification par mot de passe.
Insérez la description de l'image ici
Nous sommes rentrés dans un endroit que nous connaissons à nouveau et vérifions les deux mots de passe. L'appel des méthodes de cette classe pour vérifier le mot de passe est en fait une comparaison de parcours à un seul caractère. S'il n'est pas correct, il renvoie false,
Insérez la description de l'image ici
puis il se termine finalement. Fini avec des fleurs ~ Mettez l'adresse du projet ci-dessous, et les amis qui en ont besoin le chercheront.

Adresse de code

Adresse de code source du code, ramassez si vous le souhaitez ~

Je suppose que tu aimes

Origine blog.csdn.net/qq_41762594/article/details/107361997
conseillé
Classement