SpringBoot + Shiro se da cuenta de la autenticación de inicio de sesión (proceso de implementación + análisis del código fuente + visualización del código)

1. Información general

1.1 SpringBoot

Lo que tenemos que hacer hoy es usar SpringBoot para cooperar con Shiro para lograr la autenticación de inicio de sesión, por lo que SpringBoot es indispensable.Creo que todos pueden usar Shiro, y SpringBoot no debe ser malo, así que no entraré en demasiados detalles, principalmente presentaremos a Shiro.

1.2 Shiro

Shiro es un marco de seguridad de Java. Es potente y fácil de usar. En comparación con Spring Security, es más utilizado. Shiro tiene grandes ventajas en simplicidad y flexibilidad mientras mantiene potentes funciones.
Inserte la descripción de la imagen aquí
Las funciones de Shiro también incluyen lo siguiente:

  • Autenticación: autenticación de identidad, para verificar si un usuario tiene una identidad.
  • Autorización: Verificación de permisos, para verificar si un usuario autenticado tiene un permiso determinado. Determine "quién" puede acceder a "qué".
  • Gestión de sesiones: gestión de sesiones, gestiona la sesión después de que el usuario inicia sesión,
  • Criptografía: cifrado, utilice criptografía para cifrar datos, como contraseñas cifradas.
  • Soporte web: el soporte web se puede integrar fácilmente en el entorno web.
  • Almacenamiento en caché: almacenamiento en caché, almacenamiento en caché de datos de usuario,
  • Simultaneidad: simultaneidad, Apache Shiro admite aplicaciones multiproceso con funciones concurrentes, lo que significa que admite la verificación concurrente en aplicaciones multiproceso.
  • Testing: Testing, que proporciona soporte para pruebas.
  • Ejecutar como: permite que los usuarios inicien sesión como otros usuarios.
  • Recuérdame: ¡Recuérdame,
    entre las que es mejor comer juntas la verificación de permisos de Autenticación y Autorización!
    Hoy hablamos principalmente sobre cómo lograr la autenticación de identidad.

2. Shiro se da cuenta de la autenticación de inicio de sesión

Antes de usar Shiro para la autenticación de inicio de sesión, primero debemos comprender algunos problemas conceptuales. Primero, como un excelente marco de seguridad, Shiro debe tener un centro de administración central, que es la clase DefaultWebSecurityManager , que personalizaremos más adelante. Todos los objetos de método se agregarán a este centro de administración, que controlará los métodos que definamos para reemplazar el método de autenticación predeterminado que viene con el sistema. En segundo lugar, Realm también es un objeto muy importante y se traduce como "dominio", que actúa como un "puente" o "conector" entre Shiro y los datos de seguridad de la aplicación. El método que personalizamos debe escribirse en una clase que herede AuthorizingRealm antes de que se pueda agregar al centro de administración. El último concepto es Sujeto . Normalmente entendemos el objeto Sujeto como un usuario. De igual forma, también puede ser un programa tripartito. Es un concepto abstracto y puede entenderse como cualquier "cosa" que interactúe con el sistema es un Sujeto. Lo usaremos para iniciar sesión en la autenticación.

Importar dependencias de pom

Primero cree un proyecto SpringBoot e importe las dependencias pom shiro y springboot.

        <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>

Después de la configuración, vamos a establecer el archivo de configuración application.yml y establecemos nuestro número de puerto springboot dentro.

server:
  port: 8080  #自定义

Luego configure nuestra clase de inicio SpringBoot.

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

Configurar el método principal

Después de eso, configuramos un método de controlador para interactuar con la página, pasamos la contraseña de la cuenta para probar la situación de inicio de sesión y probar los permisos antes y después del inicio de sesión.

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";
    }
}

Después de configurar el método del Controlador, vamos a crear un Reino para configurar la autenticación de la cuenta de inicio de sesión. Cree un archivo de configuración y configure una clase MyRealm para heredar AuthorizingRealm .
Inserte la descripción de la imagen aquí
Luego implementaremos el proceso de autenticación. Después de heredar, necesitamos implementar dos métodos, uno es el método de autenticación y el otro es el método de autorización. Solo necesitamos completar el método de autenticación en esta sección.

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());
    }
}

En lo anterior, hemos completado el proceso de autenticación de la cuenta. Debe haber muchos amigos preguntando, después de la autenticación de la cuenta, ¿qué pasa con la contraseña? ¿Por qué la contraseña no se autentica en conjunto? Comencemos a escribir la contraseña de autenticación. Shiro divide la cuenta y la contraseña en dos lugares para la verificación. Si no la definimos nosotros mismos, se llamará al método de verificación predeterminado de shiro. Recreamos
una clase MyCredentialsMatcher heredando SimpleCredentialsMatcher para implementar nuestro método de verificación de contraseña personalizado.

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);
    }
}

Después de configurar la cuenta y la contraseña, necesitamos configurar un administrador para acercar nuestros componentes de verificación de cuenta y contraseña a shiro. Así es, de nuevo sale nuestra clase de configuración. La función principal de la clase de configuración es ensamblar algunos de los accesorios que hicimos antes y configurar un interceptor para interceptar las solicitudes de inicio de sesión que necesitamos para iniciar sesión. No hay mucho que decir directamente sobre el código, hay comentarios específicos en el código.

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;
    }

}

Luego podemos iniciar SpringBoot para probar. Se recomienda acceder primero a la solicitud interceptada como: / hello, y encontrará que no se puede acceder, y luego iniciar sesión: / doLogin, y luego acceder a hello. Publicaré el código fuente en github, el enlace de la dirección está en la parte inferior y se adjunta una copia de dubbo + zookeeper integrado shiro.

3. Análisis del código fuente de autenticación de inicio de sesión de Shiro

A continuación, profundicemos en cómo shiro implementa la verificación de cuenta y contraseña paso a paso. En primer lugar, debemos comenzar con el método familiar / doLogin.
Inserte la descripción de la imagen aquí
Después de ingresar, encontramos que shiro le dio el token que ingresamos al administrador de seguridad para que lo llamara
Inserte la descripción de la imagen aquí
. Después de ingresar al administrador de seguridad, encontramos que el administrador de seguridad llamó a un método de inicio de sesión, pasó nuestro token e ingresó este método para crear la cuenta Realm Certificación.
Inserte la descripción de la imagen aquí
Lo anterior es para juzgar si el token que pasamos es problemático, de lo contrario, ingrese el tema y llame al método de autenticación. A partir del informe de error a continuación, podemos entender que emitirá un juicio sobre nuestra necesidad de autenticar la cuenta.
Inserte la descripción de la imagen aquí
Luego, ven aquí, el primer paso de this.assertRealmConfigured () es un juicio sobre Realm, que juzga si Realm es un valor nulo y luego obtiene Realm. Debido a que configuramos un Reino antes, llamaremos al único Reino; de lo contrario, atravesaremos el Reino y luego llamaremos al juicio uno por uno. El siguiente es el código para llamar a varios Realms.
Inserte la descripción de la imagen aquí
Si profundizamos, podemos ver que primero juzga si el reino admite el token y, si lo hace, llama al reino para autenticación.
Inserte la descripción de la imagen aquí
Luego ingrese al reino para obtener el caché registrado previamente, si está vacío, el proceso de autenticación comenzará,
Inserte la descripción de la imagen aquí
¡Ups! Se puede volver al método con el que estamos familiarizados, que es el método que definimos antes para juzgar el nombre de usuario. Una vez que la sentencia es exitosa, el nombre de usuario y la contraseña que se encuentran en la base de datos se vuelven a empaquetar. Entonces regresa.
Inserte la descripción de la imagen aquí
Después de realizar la autenticación de la cuenta, se devuelve la información de información, que se puede ver en la imagen. Si la información no es nula, se verificará la contraseña.
Inserte la descripción de la imagen aquí
Después de ingresar, primero obtuvimos un objeto CredentialsMatcher. Para este objeto, encontramos su clase de implementación de interfaz y descubrimos que hemos implementado el método de coincidencia de contraseña, por lo que llamaremos a nuestro objeto personalizado. Por supuesto, si no personalizamos, se utilizará la contraseña de cifrado predeterminada para comparar la autenticación.
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
Luego continuamos hacia abajo y entramos en la etapa de autenticación de contraseña.
Inserte la descripción de la imagen aquí
Hemos ingresado a un lugar con el que estamos familiarizados nuevamente y verificamos las dos contraseñas. Llamar a métodos de esta clase para verificar la contraseña es en realidad una comparación transversal de un solo carácter. Si no es correcta, devuelve falso
Inserte la descripción de la imagen aquí
y finalmente termina. Terminado con flores ~ Pon la dirección del proyecto a continuación, y los amigos que lo necesiten lo recogerán.

Dirección de código

Código de la dirección del código fuente, retírelo si lo desea ~

Supongo que te gusta

Origin blog.csdn.net/qq_41762594/article/details/107361997
Recomendado
Clasificación