Maison  >  Article  >  Java  >  Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations

Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations

PHPz
PHPzavant
2023-05-16 16:55:40919parcourir

1. SpringBoot intègre Shiro

Apache Shiro est un framework de sécurité Java puissant et facile à utiliser qui effectue l'authentification, l'autorisation, les mots de passe et la gestion des sessions.

1.1, Introduction à shiro

shiro a des composants de base, à savoir Subject, SecurityManager et Realms

  • Subject : équivalent à "l'utilisateur" de l'opération en cours, cet utilisateur n'est pas forcément une personne spécifique, mais un abstrait. le concept représente tout ce qui interagit avec le programme actuel, comme les robots d'exploration, les scripts, etc. Tous les sujets sont liés au SecurityManager, et toutes les interactions avec le sujet seront déléguées au SecurityManager ; le sujet peut être considéré comme une façade ; le SecurityManager est le véritable exécuteur.

  • SecurityManager : C'est le cœur du framework Shiro. Toutes les opérations liées à la sécurité interagiront avec lui. Il gère tous les sujets.

  • Realms : agit comme un « pont » entre Shiro et les données de sécurité de l'application lors de la vérification de l'authentification (connexion) et de l'autorisation (contrôle d'accès) sur un utilisateur, SecurityManager doit obtenir l'utilisateur correspondant de Realm à des fins de comparaison afin de déterminer le. l'identité de l'utilisateur. Est-ce légal ? Il est également nécessaire d'obtenir le rôle/l'autorité correspondant de l'utilisateur auprès de Realm pour vérifier si l'utilisateur peut effectuer l'opération.

1.2, implémentation spécifique du code

1.2.1, configuration Maven

 <!--shiro-->
 <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.7.1</version>
        </dependency>
         <!--shiro整合thymeleaf-->
         <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
 <!--shiro缓存-->
  <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.7.1</version>
        </dependency>

shiro est utilisé avec jsp par défaut, et voici tous les packages jar qui doivent être importés pour intégrer thymeleaf avec shiro

1.2 . 2. Intégrez les classes à implémenter

  • De manière générale, l'intégration ne nécessite que l'implémentation de deux classes

  • L'une est ShiroConfig et l'autre est CustomerRealm

  • Si vous devez ajouter Shiro cache et ce n'est pas un cache intégré Mais le cache Redis doit également écrire deux autres classes

  • L'une est RedisCache et l'autre est RedisCacheManager

1.2.3, structure du projet

Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations

1.2. 4, implémentation de ShiroConfig

sans shiro Le cache

package com.yuwen.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.yuwen.shiro.cache.RedisCacheManager;
import com.yuwen.shiro.realm.CustomerRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
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;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {

    //让页面shiro标签生效
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

    //1、创建shiroFilter   负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //给filter设置安全管理
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        //配置系统的受限资源
        //配置系统公共资源 全部都能访问的设置anon
        Map<String,String> map = new HashMap<>();
        map.put("/main","authc");//请求这个资源需要认证和授权 authc表示需要认证后才能访问
        map.put("/admin","roles[admin]"); //表示admin角色才能访问 roles[]表示需要什么角色才能访问
        map.put("/manage","perms[user:*:*]"); //表示需要user:*:*权限才能访问 perms[]表示需要什么权限才能访问
        //访问需要认证的页面如果未登录会跳转到/login路由进行登陆
        factoryBean.setLoginUrl("/login");
        //访问未授权页面会自动跳转到/unAuth路由
        factoryBean.setUnauthorizedUrl("/unAuth");
        factoryBean.setFilterChainDefinitionMap(map);
        return factoryBean;
    }
    //2、创建安全管理器
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("getRealm") Realm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //给安全管理器设置
        securityManager.setRealm(realm);
        return securityManager;
    }
    //3、创建自定义的realm
    @Bean
    public Realm getRealm(){
        CustomerRealm customerRealm = new CustomerRealm();
        //修改凭证校验匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法为md5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //设置散列次数
        credentialsMatcher.setHashIterations(1024);
        customerRealm.setCredentialsMatcher(credentialsMatcher);
        return customerRealm;
    }
}

Parce qu'il n'est généralement pas sûr de définir des mots de passe en texte brut dans la base de données, j'ai chiffré le mot de passe avec md5 ici. Ma méthode de cryptage est : mot de passe = mot de passe + sel + nombre de. hachages puis cryptage MD5, alors créez-en un personnalisé ici. Realm doit définir un correspondant afin que le mot de passe puisse être mis en correspondance avec succès lors de la connexion

1.2.5, la mise en œuvre de CustomerRealm

package com.yuwen.shiro.realm;

import com.yuwen.pojo.User;
import com.yuwen.pojo.vo.ViewPerms;
import com.yuwen.pojo.vo.ViewRole;
import com.yuwen.service.UserService;
import com.yuwen.shiro.salt.MyByteSource;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.util.List;

//自定义realm
public class CustomerRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;
 //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取身份信息
        String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal();
        //根据主身份信息获取角色 和 权限信息
        List<ViewRole> roles = userService.findRolesByUsername(primaryPrincipal);
        if (!CollectionUtils.isEmpty(roles)){
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            roles.forEach(viewRole -> {
                simpleAuthorizationInfo.addRole(viewRole.getName());
                //权限信息
                List<ViewPerms> perms = userService.findPermsByRoleId(viewRole.getName());
                if (!CollectionUtils.isEmpty(perms)){
                    perms.forEach(viewPerms -> {
                        simpleAuthorizationInfo.addStringPermission(viewPerms.getPName());
                    });
                }
            });
            return simpleAuthorizationInfo;
        }
        return null;
    }
    
 //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获取登入的身份信息
        String principal = (String) authenticationToken.getPrincipal();
        User user = userService.findByUsername(principal);
        if (!ObjectUtils.isEmpty(user)){
            //ByteSource.Util.bytes(user.getSalt()) 通过这个工具将盐传入
            //如果身份认证验证成功,返回一个AuthenticationInfo实现;
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),new MyByteSource(user.getSalt()),this.getName());
        }
        return null;
    }
}

Cette authentification sera automatiquement appelée lors de la connexion dans. Si une erreur se produit lors de la vérification, une exception sera signalée. Je suis dans la couche contrôleur. J'ai reçu l'exception et je l'ai gérée

Gestion des exceptions lors de la connexion dans la couche contrôleur

@PostMapping("/login")
    public String login(String username,String password){
        //获取主体对象
        Subject subject = SecurityUtils.getSubject();
        try {
         //自动调用CustomerRealm 类中的身份验证方法
            subject.login(new UsernamePasswordToken(username,password));
            return "index";
        }catch (UnknownAccountException e){ //接收异常并处理
            e.printStackTrace();
            model.addAttribute("msg","用户名有误,请重新登录");
        }catch (IncorrectCredentialsException e){//接收异常并处理
            e.printStackTrace();
            model.addAttribute("msg","密码有误,请重新登录");
        }
        return "login";
    }

1.2.6, configuration du cache shiro

Le Le cache shiro est défini. Une fois que l'utilisateur s'est connecté, ses informations utilisateur et ses rôles/autorisations n'ont pas besoin d'être vérifiés à chaque fois, cela peut améliorer l'efficacité

Configuration du cache par défaut

Activer la gestion du cache dans la méthode getRealm() dans ShiroConfig

.
 @Bean
    public Realm getRealm(){
        CustomerRealm customerRealm = new CustomerRealm();
        //开启缓存管理
        customerRealm.setCacheManager(new EhCacheManager());
        //开启全局缓存
        customerRealm.setCachingEnabled(true);
        //开启认证缓存
        customerRealm.setAuthenticationCachingEnabled(true);
        customerRealm.setAuthenticationCacheName("authenticationCache");
        //开启权限缓存
        customerRealm.setAuthorizationCachingEnabled(true);
        customerRealm.setAuthorizationCacheName("authorizationCache");
        return customerRealm;
    }

Le cache intégré à reids ne sera pas expliqué ici. Vous pouvez le voir vous-même dans le code source ci-dessous

1.2.7, Paramètres index.html de la page d'accueil

Utilisez les balises ici pour déterminer quelles zones nécessitent. authentification ou quels rôles ou autorisations sont accessibles

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
                xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <link rel="shortcut icon" href="#">
</head>
<body>
<h1>index</h1>
<a href="/logout">退出</a>
<div>
    <a href="/main">main</a> | <a href="/manage">manage</a> | <a href="/admin">admin</a>
</div>
<!--获取认证信息-->
用户:<span shiro:principal=""></span><hr>
<!--认证处理-->
<span shiro:authenticated=""><hr>
    显示认证通过内容
</span>
<span shiro:notAuthenticated=""><hr>
    没有认证时 显示
</span>
<!--授权角色-->
<span shiro:hasRole="admin"><hr>
    admin角色 显示
</span>
<span shiro:hasPermission="user:*:*"><hr>
    具有用户模块的"user:*:*"权限 显示
</span>
</body>
</html>

1.3, test simple

Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations

1.3.1, rôle d'administrateur Test de toutes les autorisations

Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations

1.3.2, aucun test de rôle et d'autorisations

Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations

1.3 .3, test sans rôle et sans autorisations

Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations
Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer