>  기사  >  Java  >  SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법

SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법

PHPz
PHPz앞으로
2023-05-16 16:55:40874검색

1. SpringBoot는 Shiro를 통합합니다

Apache Shiro는 인증, 권한 부여, 비밀번호 및 세션 관리를 수행하는 강력하고 사용하기 쉬운 Java 보안 프레임워크입니다.

1.1, shiro 소개

shiro에는 Subject, SecurityManager 및 Realms라는 핵심 구성 요소가 있습니다

  • Subject: 현재 작업의 "사용자"와 동일합니다. 이 사용자는 반드시 특정 사람이 아니라 추상적인 사람입니다. 개념은 크롤러, 스크립트 등과 같이 현재 프로그램과 상호 작용하는 모든 것을 나타냅니다. 모든 주제는 SecurityManager에 바인딩되며 주제와의 모든 상호 작용은 SecurityManager에 위임됩니다. 주제는 실제 실행자로서 간주될 수 있습니다.

  • SecurityManager: 이는 Shiro 프레임워크의 핵심입니다. 모든 보안 관련 작업은 모든 주제를 관리합니다.

  • Realms: Shiro와 애플리케이션 보안 데이터 사이의 "브리지" 역할을 합니다. 사용자에 대한 인증(로그인) 및 권한 부여(액세스 제어) 확인을 수행할 때 SecurityManager는 비교를 위해 Realm에서 해당 사용자를 가져와야 합니다. 사용자의 신원이 합법적인가요? 사용자가 작업을 수행할 수 있는지 확인하려면 Realm에서 사용자의 해당 역할/권한을 얻어야 합니다.

1.2, 코드

1.2.1의 특정 구현, 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는 기본적으로 jsp와 함께 사용되며 여기에 thymeleaf를 shiro

1.2와 통합하기 위해 가져와야 하는 모든 jar 패키지가 있습니다. 2. 구현해야 할 클래스를 통합합니다

  • 일반적으로 통합은 두 클래스의 구현만 완료하면 됩니다

  • 하나는 ShiroConfig이고 다른 하나는 CustomerRealm

  • shiro를 추가해야 하는 경우 캐시이며 내장 캐시가 아닙니다. 그러나 Redis 캐시는 두 개의 다른 클래스도 작성해야 합니다

  • 하나는 RedisCache이고 다른 하나는 RedisCacheManager

1.2.3, 프로젝트 구조

SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법

1.2입니다. 4, shiro 없이 ShiroConfig

구현 캐시

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

데이터베이스에 일반 텍스트 비밀번호를 설정하는 것은 일반적으로 안전하지 않기 때문에 여기서는 md5로 비밀번호를 암호화했습니다. 비밀번호 = 비밀번호 + 소금 + 개수입니다. 해시를 사용한 다음 MD5 암호화를 수행하므로 여기에서 사용자 정의 암호화를 생성하세요. Realm은 로그인 시 비밀번호가 성공적으로 일치할 수 있도록 일치자를 설정해야 합니다.

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

이 인증은 로그인 시 자동으로 호출됩니다. 검증 중 오류가 발생하면 예외가 보고됩니다. 저는 컨트롤러 계층에서 예외를 받아 처리했습니다

컨트롤러 계층에서 로그인 중 예외 처리

@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, 시로 캐시 구성

shiro 캐시가 정의되면 사용자 정보와 역할/권한을 매번 확인할 필요가 없으므로 효율성이 향상됩니다

기본 캐시 구성

ShiroConfig의 getRealm() 메소드에서 캐시 관리를 활성화하세요

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

레이드와 통합된 캐시는 여기에서 설명하지 않습니다. 소스코드는 아래에서 확인하실 수 있습니다

1.2.7, 홈페이지 index.html 설정

여기에서 태그를 사용하여 어떤 영역을 지정할지 결정하세요. 인증이 필요하거나 액세스할 수 있는 역할 또는 권한

<!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, 단순 테스트

SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법

1.3.1, 관리자 역할 모든 권한 테스트

SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법

1.3.2, 역할 및 권한 없음 테스트

SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법

1.3.3, 역할 없음 및 권한 없음 테스트

SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법
SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법

위 내용은 SpringBoot가 Shiro를 통합하여 권한 제어를 달성하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제