스프링 시큐리티 단계별 학습 11부 URL을 동적으로 제어하는 방법은? 사용자에게 권한을 동적으로 추가하는 방법은 무엇입니까?

검토

앞에서 스프링 시큐리티가 로그인한 사용자의 권한을 인가할 수 있다고 소개했는데, 어떤 리소스에 접근할 수 있고 어떤 리소스에 접근할 수 없는지 알 수 없다면 리소스 접근 권한 설정 방법을 모르면 이전 글로 돌아가면 된다. "단계별 스프링 보안 학습 7장 , 사용자 테이블과 권한 테이블을 기반으로 권한을 구성하는 방법은? 더 많이 배울수록 더 쉬워집니다 ." 이전에 소개한 것이 구성 파일에서 직접 인터페이스를 구성하는 것임을 눈치채셨는지 모르겠습니다. 이것은 여전히 ​​많은 실제 비즈니스 시나리오에 충분하지 않습니다. 예를 들어 , 일부 기업에는 백그라운드 관리가 있을 것입니다. 기업 관리자는 백그라운드에서 첫 페이지의 메뉴를 구성하고, 어떤 메뉴에 권한을 부여하고, 어떤 사용자 ID에 권한이 있는지 이러한 메뉴에 액세스할 수 있습니다. 그렇지 않으면 액세스할 수 없습니다. 그들을. 관리자가 프런트엔드 메뉴 권한을 설정할 때 매번 코드 설정을 변경해야 한다면 비현실적일 수밖에 없는데 동적으로 설정할 수 있는 방법은 없을까? 물론 있습니다. 그것이 이 글의 주제입니다.

이 기사를 읽기 전에 이 기사를 이해하는 데 도움이 되도록 이전 기사를 읽으십시오.

  1. 인터뷰에서 스프링 보안에 익숙하지 않다고 말하지 마십시오. 데모는 면접관을 속이려고 합니다.
  2. 스프링 시큐리티를 배우기 위한 차근차근 2부, 기본 사용자를 수정하는 방법은?
  3. 단계별 스프링 보안 학습 3부, 로그인 페이지를 사용자 정의하는 방법은 무엇입니까? 로그인 콜백?
  4. 단계별 스노우 스프링 보안 4부, 로그인 과정은 어떤가요? 로그인 사용자 정보는 어디에 저장되나요?
  5. 단계별 학습 스프링 보안 5장, 리디렉션 및 서버 점프를 처리하는 방법은 무엇입니까? 로그인은 어떻게 JSON 문자열을 반환합니까?
  6. 스프링 보안 단계별 학습, 여섯 번째 장, 로그인 확인, mybatis 통합을 위해 데이터베이스에서 사용자를 읽는 방법을 알려줍니다.
  7. 스프링 시큐리티 7장 단계별로 사용자 테이블과 권한 테이블을 기반으로 권한을 설정하는 방법은? 더 많이 배울수록 더 쉬워집니다
  8. 스프링 보안 8장 단계별로 비밀번호 암호화를 구성하는 방법은 무엇입니까? 여러 암호화 체계가 지원됩니까?
  9. 스프링 시큐리티 단계별 학습, 9장, 소스 코드 해석 지원 및 다중 암호화 체계의 샘플 코드
    10. 스프링 시큐리티 단계별 학습, 10장 토큰으로 로그인하는 방법은? JWT 데뷔

동적 권한이 필요한 이유는 무엇입니까?

한마디로 : 기업의 비즈니스 요구를 유연하게 충족시키기 위해
예를 들어

대부분의 회사는 소규모에서 대규모로 성장하는 과정에 있습니다.초기에는 회사에 10명밖에 없었고 Xiao Zhang은 HR 부서의 유일한 사람이었습니다.Xiao Zhang은 재무, 채용 등 모든 역할을 담당했습니다. , 프런트 데스크 보기 권한

나중에 회사는 점차 확장되었고 인사 부서에도 Xiao Li와 Xiao Wang 2 명을 추가했습니다.모집, 재무, 프런트 데스크 등 모든 권한, 결석 한 Xiao Li의 작업 항목을 언제든지 확인하거나 운영 할 수 있습니다. 오늘 퇴근한 사람, 출장비 환급 신청한 사람, 환급 금액, 소규모 Wang 채용 및 프론트 데스크 업무를 보거나 운영할 수 있으며, Xiao Li는 재무를 담당하고 있기 때문에 금융 사업만 보고 운영할 수 있지만 Xiao Wang의 사업을 보고 운영할 수 있는 권한을 초과할 수 없습니다.

회사는 계속 확장되고 공개됩니다. 현재 HR 부서에는 1,000명이 있고 많은 지점이 있습니다. 사람들은 매일 가입하고 떠날 수 있으며 권한을 수정해야 합니다. 이전에 소개한 내용을 따르는 경우 URL을 추가하십시오. 구성 파일에서 권한 구성이 죽었고 매번 다시 시작한 후에 적용되므로 의심 할 여지없이 사람들이 충돌합니다

권한을 더 잘 관리하는 방법은 무엇입니까?

데이터베이스 설계

권한을 잘 관리하려면 먼저 데이터베이스를 잘 설계해야 권한 관리가 매우 간단해집니다.

그리고 데이터베이스를 설계하는 방법은 무엇입니까? 가장 중요한 점은 격리입니다. 즉, 테이블은 하나의 비즈니스만 나타내며 다른 비즈니스와 혼합되어서는 안 됩니다.

첫째, 있어야합니다

사용자 테이블 정의

CREATE TABLE `h_user` (
                          `id` int NOT NULL AUTO_INCREMENT,
                          `username` varchar(50) NOT NULL COMMENT '用户名',
                          `password` varchar(500) NOT NULL COMMENT '密码',
                          `enabled` tinyint(1) NOT NULL COMMENT '是否启动,0-不启用,1-启用',
                          PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT '用户表';

역할 테이블 정의

CREATE TABLE `h_role` (
                                 `id` int NOT NULL AUTO_INCREMENT,
                                 `name` varchar(50) NOT NULL COMMENT '角色名称',
                                 `code` varchar(50) NOT NULL COMMENT '角色编码',
                                 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT '角色表';

역할은 실제로 권한입니다.

메뉴 테이블

CREATE TABLE `h_menu` (
                          `id` int NOT NULL AUTO_INCREMENT,
                          `name` varchar(50) NOT NULL COMMENT '菜单名称',
                          `url` varchar(200) NOT NULL COMMENT '菜单URL',
                          `parent_id` int NOT NULL default 0 COMMENT '上级菜单id',
                          PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT '菜单表';

메뉴 테이블은 어떤 메뉴를 관리할 권한으로 넘겨야 하는지 정의하고, URL은 권한 관리 인터페이스로 넘겨준다.

역할 인원 테이블

CREATE TABLE `h_role_user` (
                               `role_id` int NOT NULL COMMENT '角色id',
                               `user_id` int NOT NULL COMMENT '菜单id'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT '角色用户表';

역할 메뉴 테이블

CREATE TABLE `h_role_menu` (
                               `role_id` int NOT NULL COMMENT '角色id',
                               `menu_id` int NOT NULL COMMENT '菜单id'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT '角色菜单表';

개인, 역할 및 메뉴 테이블 간의 관계가 분리된 이유는 무엇입니까? 메뉴 테이블을 역할 테이블에 병합하지 않는 이유는 무엇입니까? 역할 테이블에 사용자 ID를 저장하는 것이 좋지 않을까요?
사실 이렇게 하면 정말 안 좋은데 예전에 재무를 담당하던 사용자가 지금은 다른 부서로 옮겨져 지금은 회계와 정산을 담당하고 있다면 사용자의 역할을 삭제해야 하고, 그런 다음 역할에 대한 두 개의 새 레코드를 만들고 역할 이름, 역할 코드, 메뉴 URL을 채우고 사람을 선택하는 등의 작업을 수행합니다.

하지만 이 디자인은 어떨까요?
역할과 메뉴의 관계는 기본적으로 변경되지 않으며, 설정 후에도 기본적으로 변경되지 않습니다. 주요 변화는 사람입니다.오늘은 1,000명의 신규 직원을 채용하거나 500명의 직원을 떠날 수 있지만 역할 사용자 테이블만 유지하면 되므로 더 쉽지 않습니까?

자, 이 테이블에 일부 초기 데이터를 추가해 보겠습니다.

INSERT INTO `h_user` (`username`, `password`, `enabled`) VALUES ('harry', '{bcrypt}$2a$10$gQv1oUFK/LvbV7p4Nk0xE.Gn8H1lYV1hqVJfReWSUYUQBfCkGq2uy', '1');
INSERT INTO `h_user` (`username`, `password`, `enabled`) VALUES ('mike', '{bcrypt}$2a$10$gQv1oUFK/LvbV7p4Nk0xE.Gn8H1lYV1hqVJfReWSUYUQBfCkGq2uy', '1');


INSERT INTO `h_role` (`name`, `code`) VALUES ('超级管理员', 'admin'),('用户管理员','user');

INSERT INTO `h_menu` (`name`, `url`) VALUES ('后台管理', '/admin'),('用户管理','/user/**');

INSERT INTO `h_role_menu` (`role_id`, `menu_id`) VALUES (1, 1),(1,2),(2,2);

INSERT INTO `h_role_user` (`role_id`, `user_id`) VALUES (1, 1),(2,2);
  • 두 명의 사용자 harry와 mike가 각각 ID 1과 2로 초기화됩니다.
  • 슈퍼 관리자와 사용자 관리자의 두 가지 역할이 초기화되며 여기의 코드는 실제로 권한입니다.
  • 백그라운드 관리와 사용자 관리 2가지 메뉴 초기화
  • 세 가지 역할 메뉴 레코드가 초기화됨
  • User: harry는 최고관리자 역할, Mike: 사용자는 관리자 역할이므로 사용자 harry는 백그라운드 관리 및 사용자 관리 메뉴에 접근할 수 있고, mike는 이것만 접근할 수 있습니다. 메뉴 또는 인터페이스

자, 데이터베이스 설계에 대해 이야기 한 후 구현 방법을 소개하겠습니다.

동적 권한 관리를 구현하는 방법은 무엇입니까?

동적으로 URL 권한 구성 얻기

MenuFilterInvocationSecurityMetadataSource 클래스를 생성하여 FilterInvocationSecurityMetadataSource 인터페이스의 Collection getAttributes(Object object) 메서드를 구현합니다. 이 구현 클래스는 주로 데이터베이스에서 메뉴의 권한을 읽고 권한을 동적으로 로드합니다.

권위의 판단

MenuAccessDecisionManager 클래스를 생성하여 메뉴 또는 인터페이스의 권한을 확인할 수 있는 AccessDecisionManager 인터페이스의 결정(인증 인증, 개체 개체, 컬렉션 컬렉션) 메서드를 구현합니다.

좋아, 프로젝트 구축을 시작하고 기적을 목격하자

빌드 프로젝트

프로젝트 생성: security-mybatis-jwt-perm

이 프로젝트는 이전 기사 " 스프링 보안 단계별 학습, 챕터 10 토큰으로 로그인하는 방법?"을 기반으로 합니다. JWT데뷔 ' 변신 프로젝트

Maven 종속성을 추가합니다.

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.18</version>
            <scope>test</scope>
        </dependency>

이 종속성은 주로 컬렉션 및 문자열 툴킷과 같은 내부 툴킷을 사용합니다.

pom 프로젝트 이름과 artifactId를 security-mybatis-jwt-perm으로 통일되도록 수정합니다.

새로운 데이터베이스 운영 관련 클래스 및 매퍼

엔터티 클래스 만들기

  • 메뉴 클래스 만들기
@Data
public class Menu {
    
    
    //菜单id
    private int id;
    //菜单名称
    private String name;
    //菜单URL
    private String url;
    //上级菜单id
    private int parentId;
}
  • Role 클래스를 생성하는 것은 실제로 원래 Authorities 클래스를 Role 클래스로 변경한 다음 몇 개의 필드를 추가하는 것입니다.
public class Role implements GrantedAuthority {
    
    
    private int id;

    private String name;
    //权限编码
    private String code;

    @Override
    public String getAuthority() {
    
    
        return "ROLE_"+code;
    }
}
  • 사용자 클래스 수정
@Data
public class User implements UserDetails {
    
    
    private int id;
    private String password;
    private String username;
    private boolean accountNonExpired=true;
    private boolean accountNonLocked=true;
    private boolean credentialsNonExpired=true;
    private boolean enabled;

    private List<Role>authorities;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
    
    
        return authorities;
    }
    @Override
    public String getPassword() {
    
    
        return password;
    }
    @Override
    public String getUsername() {
    
    
        return username;
    }
    @Override
    public boolean isAccountNonExpired() {
    
    
        return accountNonExpired;
    }
    @Override
    public boolean isAccountNonLocked() {
    
    
        return accountNonLocked;
    }
    @Override
    public boolean isCredentialsNonExpired() {
    
    
        return credentialsNonExpired;
    }
    @Override
    public boolean isEnabled() {
    
    
        return enabled;
    }
}

mybatis의 mapper.xml 추가

  • RoleMapper.xml 추가
<mapper namespace="com.harry.security.mapper.RoleMapper">

    <select id="findRolesByUserId" resultType="com.harry.security.entity.Role" parameterType="integer">
        select role.id,role.`name`,role.`code` from h_role role
        LEFT JOIN h_role_user ru on ru.role_id=role.id
        WHERE ru.user_id=#{userId}
    </select>

    <select id="findRolesByUrl" resultType="com.harry.security.entity.Role" parameterType="string">
        select role.id,role.`name`,role.`code` from h_role role
        LEFT JOIN h_role_menu rm on rm.role_id=role.id
        LEFT JOIN h_menu m on m.id=rm.menu_id
        WHERE m.url=#{url};
    </select>
</mapper>

여기에 두 가지 쿼리가 제공됩니다.

  1. findRolesByUserId는 사용자 ID에 따라 사용자가 가진 역할을 쿼리하는 것입니다.
  2. findRolesByUrl은 메뉴 URL에 따라 메뉴가 속한 역할을 쿼리하는 것입니다.
  • MenuMapper.xml 추가
<mapper namespace="com.harry.security.mapper.MenuMapper">

    <select id="findMenusByRoleId" resultType="com.harry.security.entity.Menu" parameterType="integer">
        SELECT m.id,m.`name`,m.parent_id,m.url from h_menu m
        LEFT JOIN h_role_menu rm
        on rm.menu_id=m.id
        WHERE rm.role_id=#{roleId};
    </select>
    <select id="findAllMenus" resultType="com.harry.security.entity.Menu">
        SELECT m.id,m.`name`,m.parent_id,m.url from h_menu m
    </select>

</mapper>

여기에 제공된 두 개의 쿼리 인터페이스도 있습니다.

  1. findMenusByRoleId 메서드는 들어오는 역할 ID에 따라 역할 아래의 관리 메뉴를 쿼리하는 것입니다.
  2. findAllMenus 메소드는 모든 메뉴를 조회하는 것입니다.
  • UserMapper.xml 수정
<mapper namespace="com.harry.security.mapper.UserMapper">

    <resultMap id="BaseUser" type="com.harry.security.entity.User" >
        <id property="id" column="id" ></id>
        <result property="username" column="username" ></result>
        <result property="password" column="password" ></result>
        <result property="enabled" column="enabled" ></result>
    </resultMap>
    <select id="findUserByUsername" resultMap="BaseUser" parameterType="string">
        select u.id,u.username,u.`password`,u.enabled from h_user u where u.username=#{username};
    </select>

</mapper>

사용자는 사용자 이름을 기반으로 사용자 정보를 쿼리할 수 있는 인터페이스가 하나뿐입니다.

매퍼 인터페이스 추가

  • RoleMapper 인터페이스 생성
@Mapper//指定这是一个操作数据库的mapper
public interface RoleMapper {
    
    
    /**
     * 根据用户id查找角色
     * @param userId
     * @return
     */
    List<Role> findRolesByUserId(int userId);

    /**
     * 根据URL查找角色
     * @param url
     * @return
     */
    List<Role> findRolesByUrl(String url);
}
  • 다음을 통해 MenuMapper 인터페이스 생성
@Mapper//指定这是一个操作数据库的mapper
public interface MenuMapper {
    
    

    /**
     * 根据角色id查找菜单
     * @param roleId
     * @return
     */
    List<Menu> findMenusByRoleId(int roleId);

    /**
     * 查询所有配置菜单
     * @return
     */
    List<Menu> findAllMenus();
}
  • UserMapper 인터페이스가 이미 존재하므로 변경할 필요가 없습니다.

동적 권한 구성

동적으로 URL 권한 구성 얻기

MenuFilterInvocationSecurityMetadataSource 클래스를 만들고 FilterInvocationSecurityMetadataSource 인터페이스를 구현합니다.

public class MenuFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    
    
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    
    
        Set<ConfigAttribute> set = new HashSet<>();
        // 获取请求地址
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        log.info("requestUrl >> {}", requestUrl);
        List<Menu> allMenus = menuMapper.findAllMenus();
        if (!CollectionUtils.isEmpty(allMenus)) {
    
    
            List<String> urlList = allMenus.stream().filter(f->f.getUrl().endsWith("**")?requestUrl.startsWith(f.getUrl().substring(0,f.getUrl().lastIndexOf("/"))):requestUrl.equals(f.getUrl())).map(menu -> menu.getUrl()).collect(Collectors.toList());
            for (String url:urlList){
    
    
                List<Role> roles = roleMapper.findRolesByUrl(url); //当前请求需要的权限
                if(!CollectionUtils.isEmpty(roles)){
    
    
                    roles.forEach(role -> {
    
    
                        SecurityConfig securityConfig = new SecurityConfig(role.getAuthority());
                        set.add(securityConfig);
                    });
                }
            }
        }
        if (ObjectUtils.isEmpty(set)) {
    
    
            return SecurityConfig.createList("ROLE_LOGIN");
        }
        return set;
    }
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
    
    
        return null;
    }
    @Override
    public boolean supports(Class<?> clazz) {
    
    
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

여기서는 주로 Collection getAttributes(Object object) 메서드를 구현하여 데이터베이스에서 메뉴 권한을 동적으로 로드합니다.

  • 먼저 데이터베이스에서 모든 메뉴를 조회한 후 필터링하여 현재 요청을 만족하는 URL을 찾습니다. 매칭 방법에 정확히 일치하는 항목이 있거나 메뉴 구성이 **로 종료되어 퍼지 매칭 경로를 식별합니다. 이전 일치 요구 사항 액세스 제어를 충족하므로
  • 그러면 필터링 후 만족하는 메뉴 URL에 따라 자신의 역할을 질의하고 제어가 필요한 메뉴의 역할을 반환함으로써 현재 접속한 요청 URL의 동적 설정이 완료된다.
  • 마지막으로, 현재 요청 URL에 해당 역할이 구성되어 있지 않은 경우, 즉 집합 모음이 비어 있으면 기본 역할이 반환되며 주로 현재 요청 URL의 기본 역할을 식별하기 위해 사용자 정의할 수 있습니다. 기본 역할이 지정되지 않은 경우 기본적으로 시스템은 익명의 사용자에게 모든 인터페이스에 대한 액세스 권한을 부여하며 요청은 권한 제어를 위한 권한 판단 구현 클래스에 들어갈 수 없습니다.로그인하지 않아도 모든 액세스가 가능합니다. 불합리한 인터페이스입니다.이 점에 유의해야합니다.
  • 다른 두 메서드의 구현은 다음과 같이 작성할 수 있습니다. 걱정하지 마세요.

여기서 주의할 점은 메뉴권한은 매번 데이터베이스 전체를 조회하는 것입니다 데이터가 많으면 성능에 영향을 줄 수 있습니다 여기에서 읽기 캐시를 수정할 수 있지만 추가할 때 캐시 데이터를 업데이트하고 메뉴 수정.

동적 권한 판단

MenuAccessDecisionManager 클래스를 만들어 AccessDecisionManager 인터페이스를 구현합니다.

public class MenuAccessDecisionManager implements AccessDecisionManager {
    
    

    @Autowired
    private RoleMapper roleMapper;
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
    
    
// 当前请求需要的权限
        log.info("collection:{}", collection);
        log.info("principal:{} authorities:{}", authentication.getPrincipal().toString());
        Object principal = authentication.getPrincipal();
        if(principal instanceof String){
    
    
            throw new BadCredentialsException("未登录");
        }

        List<Role> roleList=null;
        for (ConfigAttribute configAttribute : collection) {
    
    
            // 当前请求需要的权限
            String needRole = configAttribute.getAttribute();
            if ("ROLE_LOGIN".equals(needRole)) {
    
    
                return;
            }

            // 当前用户所具有的权限
            if(roleList==null){
    
    
                User loginUser= (User) authentication.getPrincipal();
                roleList = roleMapper.findRolesByUserId(loginUser.getId());
            }
            for (GrantedAuthority grantedAuthority : roleList) {
    
    
                // 包含其中一个角色即可访问
                if (grantedAuthority.getAuthority().equals(needRole)) {
    
    
                    return;
                }
            }
        }
        throw new AccessDeniedException("SimpleGrantedAuthority!!");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
    
    
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
    
    
        return true;
    }
}

여기서 주요 구현 방법은 다음과 같습니다. 결정(인증 인증, 개체 개체, 컬렉션 컬렉션)

  • collection은 현재 요청 URL의 권한 모음입니다.
  • 먼저 사용자가 로그인했는지 확인합니다. 그렇지 않으면 기본적으로 익명 사용자가 됩니다.authentication.getPrincipal()은 문자열을 가져오고 로그인 후 User 개체를 가져와야 합니다. 로그인, throw 현재 사용자가 로그인하지 않았음을 프런트 엔드에 알리는 예외가 발생합니다. 프런트 엔드는 무엇을 하고 있습니까?
  • 둘째, 컬렉션을 반복하고 권한을 제거합니다.
  • 기본 권한인 경우 제어 없이 바로 전달
  • 데이터베이스에서 현재 로그인한 사용자의 권한을 쿼리합니다.이 목적은 사용자 권한이 변경될 때 최신 사용자 권한 데이터를 적시에 가져오는 것을 방지하여 권한 제어의 적시성을 보장하는 것입니다.
  • 메뉴 구성 권한인 경우 현재 로그인한 사용자에게 권한이 있는지 판단하여 권한이 있는 경우 스킵
  • 현재 로그인한 사용자에게 권한이 없으면 이 인터페이스에 대한 권한이 없음을 나타내는 예외가 발생하며 액세스가 거부됩니다.

SecurityConfig 구성

 protected void configure(HttpSecurity http) throws Exception {
    
    
        http.authorizeRequests()
                .anyRequest().authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
    
    
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
    
    
                        object.setSecurityMetadataSource(menuFilterInvocationSecurityMetadataSource); //动态获取url权限配置
                        object.setAccessDecisionManager(menuAccessDecisionManager); //权限判断
                        return object;
                    }
                })
                .and().formLogin()
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
        .permitAll()
                .and().exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .and().logout().logoutSuccessHandler(logoutSuccessHandler)
                .and().csrf().disable()
        ;
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

    }

이것은 주로 연관된 동적 권한 구현 클래스를 구성하기 위한 것입니다.

이렇게 해서 기본적으로 모든 설정을 마쳤고, 나머지 인터페이스 정의는 아직 이전 글 " 스프링 보안 단계별 학습 10장 토큰으로 로그인하는 방법?" 에 있습니다. JWT Shining Debut '는 동일하며 변경된 부분은 위에서 설명한 바 있으며 효과를 확인하기 위해 테스트를 시작합니다.

테스트 시작

프로젝트를 시작하고 mike로 로그인하여 승인된 인터페이스에 액세스하면 정상적으로 액세스할 수 있습니다.
여기에 이미지 설명 삽입

권한이 없는 인터페이스에 대한 액세스는 정상적으로 액세스할 수 없습니다.
여기에 이미지 설명 삽입

이때 회사 직원의 직위 조정을 시뮬레이션합니다.데이터베이스에 역할 사용자 레코드를 추가하는 것은 Mike에게 최고 관리자 권한을 추가하는 것입니다.
INSERT INTO h_role_user( role_id, user_id) VALUES (1, 2);

그런 다음 다시 방문하면 방문이 성공합니다. 즉, 동적 권한 제어를 완료한
여기에 이미지 설명 삽입
다음 위에 추가된 역할 사용자 레코드를 삭제하고 삭제 후 권한이 없으면 harry 사용자로 로그인합니다. 기대효과와 일치하는

사실 제가 쓴 글들은 주로 프론트엔드와 백엔드의 분리에 치우쳐 있다는 것을 알게 되실 겁니다. 프런트엔드 지원입니다. 오시겠습니까? 사용자 데이터, 메뉴, 역할 등의 추가를 완료하기 위해 등록 페이지 상호 작용과 결합된 등록 인터페이스를 추가할 수 있습니다. 이 문서의 내용이 아닙니다. 실제 비즈니스를 기반으로 직접 추가하십시오. 필요

좋아, 됐어, 우리는 동적 권한 제어를 완료했습니다.상장 회사라도 매일 수천 명의 직원이 입사하고 수천 명의 직원이 퇴사하더라도 역할 사용자 테이블 만 유지하면 권한 제어가 완료됩니다.

소스 코드 다운로드

추천

출처blog.csdn.net/huangxuanheng/article/details/119302215