springboot combinado con shiro combate real - autenticación de identidad

 

Tabla de contenido

Construcción del entorno

Configurar el entorno shiro

Resumir


Hola a todos, cuando escribimos cualquier proyecto de nivel empresarial, básicamente necesitamos permisos, que incluyen autenticación y autorización de identidad.

La llamada autenticación de identidad es para demostrar que usted es usted.

La llamada autorización es para comprender qué puede hacer después de iniciar sesión.

Ahora, comencemos con el proyecto Springboot y combinémoslo con el marco Shiro para hacerlo todo.

Construcción del entorno

Información básica:

versión de arranque de primavera:

Otras dependencias iniciales:

Creado:

Agregue dependencias para Shiro y Spring Boot. Agregue las siguientes dependencias a su archivo pom.xml:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.7.1</version>
</dependency>

Configurar el entorno shiro

El primer paso es crear una clase de configuración Shiro.

Cree una clase y márquela con la anotación @Configuration. En esta clase, puede configurar ajustes relacionados de Shiro, como Realm, Administrador de sesiones, etc.

El código de configuración es el siguiente:

package com.it.shirodemo.config;

import com.it.shirodemo.realm.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {

    @Bean
    public MyRealm myRealm(){
        return new MyRealm();
    }


    @Bean(name = "mySecurityManager")
    public DefaultWebSecurityManager  securityManager(@Qualifier("myRealm") MyRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        return securityManager;
    }

    /**
     * 路径过滤规则
     * @return
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        //登录不需要校验权限
        chainDefinition.addPathDefinition("/login", "anon");
        //其他任何url都需要校验是否登录
        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("mySecurityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);
        filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());
        return filterFactoryBean;
    }

    /**
     * 开启Shiro注解模式,可以在Controller中的方法上添加注解
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("mySecurityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

Estos son códigos relativamente fijos y ni siquiera es necesario conocer el significado de cada frijol, no hay problema en copiar el pasado y usarlo directamente.

Luego, necesitamos personalizar un Reino.

el código

package com.it.shirodemo.realm;

import com.it.shirodemo.entity.User;
import com.it.shirodemo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;

import javax.annotation.Resource;


public class MyRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 实现授权逻辑
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获得当前subject
        Subject subject = SecurityUtils.getSubject();
        //获得当前的principal,也就是认证完后我们放入的信息
        User currentUser = (User) subject.getPrincipal();
        //添加权限
        info.addStringPermissions(currentUser.getPerms());

        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 实现认证逻辑
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //从数据库中查询该用户
        String username = usernamePasswordToken.getUsername();
        User user = userService.queryUserByName(username);
        //如果不存在该用户,返回一个空错误,前端也可以相应显示提示
        if (user == null) {
            return null;
        }
        // 第一个参数为principal,就是授权方法里面拿到的那个对象;
        // 第二个参数为从数据库中查出的用于验证的密码,shiro中密码验证不需要我们自己去做;
        // 第三个参数为 realmName
        return new SimpleAuthenticationInfo(user,user.getPwd(),getName());
    }
}

Es principalmente para escribir métodos de autorización y autenticación. Lo anterior se doGetAuthorizationInfoutiliza para la autorización. Se llamará cada vez que visite una determinada URL del sistema. El objetivo es obtener los permisos del usuario actual.

Eche un vistazo a la clase Usuario:

el código

package com.it.shirodemo.entity;

import lombok.Data;
import java.util.Set;

@Data
public class User {
    private String userName;
    private String pwd;
    private Set<String> perms;
}

perms es una colección Set, inicializamos y consultamos en el servicio simulado:

package com.it.shirodemo.service;

import com.it.shirodemo.entity.User;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserService {


    public User queryUserByName(String username) {
        List<User> users = new ArrayList<>();
        users.add(new User(){
   
   {
            setUserName("admin");
            setPwd("123");
            setPerms(new HashSet<String>(){
   
   {
                add("shiro:user-query");
                add("shiro:user-add");
                add("shiro:user-delete");
                add("shiro:user-edit");
            }});
        }});

        users.add(new User(){
   
   {
            setUserName("zhangsan");
            setPwd("123");
            setPerms(new HashSet<String>(){
   
   {
                add("shiro:user-query");
            }});
        }});
        List<User> userList = users.stream().filter(e -> e.getUserName().equals(username)).collect(Collectors.toList());
        if(userList.size() > 0){
            return userList.get(0);
        }
        return null;

    }
}

Para realizar pruebas, creamos especialmente un controlador de usuario.

package com.it.shirodemo.controller;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/user/")
public class UserController {

    @GetMapping("/query")
    @RequiresPermissions("shiro:user-query")
    public String query(){
       return "用户查询";
    }

    @GetMapping("/edit")
    @RequiresPermissions("shiro:user-edit")
    public String edit(){
        return "用户修改";
    }

    @GetMapping("/delete")
    @RequiresPermissions("shiro:user-delete")
    public String delete(){
        return "用户删除";
    }

    @GetMapping("/add")
    @RequiresPermissions("shiro:user-add")
    public String add(){
        return "用户新增";
    }
}

Esperamos que el usuario administrador pueda acceder a todos los permisos y que zhangsan solo pueda acceder a la interfaz que el usuario consulta.

Ejecutado

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    // 实现授权逻辑
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //获得当前subject
    Subject subject = SecurityUtils.getSubject();
    //获得当前的principal,也就是认证完后我们放入的信息
    User currentUser = (User) subject.getPrincipal();
    //添加权限
    info.addStringPermissions(currentUser.getPerms());

    return info;
}

Después de este método de autorización, el objeto de información de autorización devuelto tiene una lista de todos los permisos. Si contiene sus permisos de acceso actuales, por ejemplo, el shiro:user-addacceso está permitido; de lo contrario, el acceso no está permitido.

Veamos nuevamente el método de autenticación: para decirlo sin rodeos, la autenticación es la verificación de inicio de sesión.

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    // 实现认证逻辑
    UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
    //从数据库中查询该用户
    String username = usernamePasswordToken.getUsername();
    User user = userService.queryUserByName(username);
    //如果不存在该用户,返回一个空错误,前端也可以相应显示提示
    if (user == null) {
        return null;
    }
    // 第一个参数为principal,就是授权方法里面拿到的那个对象;
    // 第二个参数为从数据库中查出的用于验证的密码,shiro中密码验证不需要我们自己去做;
    // 第三个参数为 realmName
    return new SimpleAuthenticationInfo(user,user.getPwd(),getName());
}

Aquí hay algo muy interesante: el objeto SimpleAuthenticationInfo que finalmente se devuelve, el primer parámetro del método de construcción es un tipo de objeto.

Esta cosa es en realidad:

En otras palabras, qué parámetros se pasan en el método de autenticación y qué objeto se obtiene como principal en el método de autorización.

¿Por qué proporcionar tal apertura para que usted pase el objeto? De hecho, es para permitirle obtener la lista de autorización del usuario actual al autorizar.

He visto muchos artículos sobre Shiro en Internet y mi comprensión de este punto es bastante vaga. De todos modos, solo recuerde que no importa lo que pase en este parámetro, debe poder obtener la lista de permisos a través de él. Por ejemplo, pasaré un objeto de usuario aquí y ya hay permisos en el objeto de usuario, por lo que no hay problema. También puede pasar el nombre de usuario y luego ir a la base de datos para consultar la lista de permisos al realizar la autorización, y está completamente bien.

Finalmente, proporcione el método de inicio de sesión:

package com.it.shirodemo.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {

    @GetMapping("/login")
    public String login(String username,String password){
        Subject subject = SecurityUtils.getSubject();
        //令牌
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            //登录认证
            subject.login(token);
            return "恭喜,登录成功!";
        }catch (UnknownAccountException e){
            return "账号不存在";
        }catch (IncorrectCredentialsException e){
            return "密码错误";
        }

    }
}

Para verificar, abra el navegador, porque es una solicitud de obtención, usamos el navegador para enviarla directamente.

http://localhost:8080/login?username=admin&password=123

Luego vaya a visitar: http://localhost:8080/v1/user/add

Si Zhang San inicia sesión, visite http://localhost:8080/v1/user/add

El fondo informó un error:

org.apache.shiro.authz.AuthorizationException: no autorizado para invocar el método: public java.lang.String com.it.shirodemo.controller.UserController.add()

Resumir

Este artículo implementa el llamado control de permisos ACL, que se implementa con el marco shiro combinado con springboot, que es muy adecuado para que aprendan los principiantes.

Descarga del código fuente https://gitee.com/skyblue0678/shiro-demo

Supongo que te gusta

Origin blog.csdn.net/weixin_39570751/article/details/132317373
Recomendado
Clasificación