Maison >Java >javaDidacticiel >Comment SpringBoot intègre Shiro pour obtenir le contrôle des autorisations
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.
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.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
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.1, rôle d'administrateur Test de toutes les autorisations
1.3.2, aucun test de rôle et d'autorisations
1.3 .3, test sans rôle et sans 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!