基于SpringSecurity打造企业级权限管理系统笔记

第1章 课程整体概述与权限管理系统介绍

1-2 为什么企业级项目需要权限管理?

在这里插入图片描述

1-3 权限管理核心

在这里插入图片描述
在这里插入图片描述

1-4 理想中的权限管理–基于RBAC

在这里插入图片描述
权限管理页面包含哪些??
在这里插入图片描述

1-5 开源权限管理项目

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1-6 课程基本介绍

在这里插入图片描述
在这里插入图片描述

第2章 Spring Security权限框架学习与演练

2-1 Spring Security权限管理框架介绍

在这里插入图片描述
在这里插入图片描述

2-2 Spring Security常用权限拦截器讲解

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
HttpSessionContextIntegrationFilter位于过滤器的顶端,
是第一个执行的过滤器。
用途1: 在执行其他过滤器前,判断用户的session是否已经存在了一个springSecurity上下文的 SecurityContext
如果存在就将 SecurityContext取出来 放到 SecurityContextHolder中,供 springSecurity其他部分使用。
如果不存在就创建出一个 SecurityContext 出来,放到SecurityContextHolder中,供 springSecurity其他部分使用。

SecurityContextPersistenceFilter中的源码部分
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
				response);
		SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
		try {
			SecurityContextHolder.setContext(contextBeforeChainExecution);

			chain.doFilter(holder.getRequest(), holder.getResponse());

		}
		// Crucial removal of SecurityContextHolder contents - do this before anything
		// else.
			SecurityContextHolder.clearContext();

用途2: 在所有过滤器执行完毕后,清空SecurityContextHolder中的内容
因为SecurityContextHolder是基于ThreadLocal的,如果在操作完成后,没有清空ThreadLocal,会受到服务器线程池机制的影响。

ThreadLocal分析源码部分
ThreadLocal 存放值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递,这样处理后能够优雅的解决实际中的并发问题,
1.set()方法
ThreadLocalMap是ThreadLocal中的静态内部类
static class ThreadLocalMap{}

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }

在这里插入图片描述
主要用于:用户发送注销请求时,销毁用户session,情况securityContextHolder,重定向到注销成功页面,
在这里插入图片描述
用于处理Form登录的过滤器,与Form登录的所有操作都在此进行。

  • 处理spring-security-check请求,使用用户登录的提交地址。
  • 操作: 通过用户名和密码判断此用户是否有效,登录成功跳转到成功页面 ,登录失败跳转到失败页面
    在这里插入图片描述
    用来生成默认登录页面
    在这里插入图片描述
    在这里插入图片描述
    用来包装用户的请求
    在这里插入图片描述
    实现Remember me功能
    在这里插入图片描述
    分配匿名用户权限
    在这里插入图片描述
    全局异常处理
    在这里插入图片描述
    防御会话攻击
    在这里插入图片描述

2-3 Spring Security数据库管理讲解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Authentication 对象是SpringSecurity使用的进行安全访问的控制用户信息的安全对象
它有未认证和已认证两种状态
UserDetails代表用户信息源

2-4 Spring Security权限缓存讲解

CachingUserDetailsService
EhCacheBasedUserCache

2-5 Spring Security自定义决策讲解

在这里插入图片描述
public abstract class AbstractAccessDecisionManager

投票器
public interface AccessDecisionVoter<S> {
	// ~ Static fields/initializers
	// =====================================================================================

	int ACCESS_GRANTED = 1;
	int ACCESS_ABSTAIN = 0;
	int ACCESS_DENIED = -1;
	

实现类
public class RoleVoter implements AccessDecisionVoter

三个投票器:
1.AffirmativeBased : 一票通过
2. ConsensusBased : 有一半及以上投票通过
3. UnanimousBased :所有投票器都通过才允许访问

2-6 基于SpringBoot的SpringSecurity环境快速搭建与验证

  1. 创建项目工程
    POM.XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.8</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.mall</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>demo</name>
	<description>Demo project for Spring Security</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
  1. 在主启动类上面添加请求访问测试路径
package com.mall.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@RequestMapping("/")
	public String home(){
		return "HELLO SPRINGBOOT";
	}

	@RequestMapping("/hello")
	public String hello(){
		return "hello";
	}
}
  1. 创建SpringSecurity的配置类,并配置相关校验信息
package com.mall.demo;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 有关HTTP请求哪些请求会被拦截,以及请求该怎么处理
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    // 项目主路径可放行
                    .antMatchers("/").permitAll()
                    // 其他请求要全部经过验证处理
                    .anyRequest().authenticated()
                    // 注销允许访问
                    .and()
                    .logout().permitAll()
                    // 支持表单登录
                    .and()
                    .formLogin();
            //关闭csrf认证
            http.csrf().disable();
    }

    /**
     * 忽略静态资源的拦截
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/css/**","/images/**");
    }
}

2-8 基于SpringSecurity权限管理Case实操

  1. 可以进行登录操作,在SpringSecurity的配置类中添加方法
  /**
     * SpringSecurity基于内存的验证方法
     * 这是因为Spring boot 2.0.3引用的security 依赖是 spring security 5.X版本,此版本需要提供一个PasswordEncorder的实例,否则后台汇报错误:
     * java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
     * 并且页面毫无响应。
     *
     * 因此,需要创建PasswordEncorder的实现类。
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       auth.inMemoryAuthentication()
               // 这样,页面提交时候,密码以明文的方式进行匹配。
               // todo : 需要添加该passwordEncoder()
               .passwordEncoder(new MyPasswordEncoder())
               // 指定用户
               .withUser("admin")
               // 指定用户密码
               .password("123456")
               // 指定角色
               .roles("ADMIN");
    }
  1. 实现不同用户拥有不同的访问资源权限
// 2.1 添加一个访问路径方法
@PreAuthorize("hasRole('ROLE_ADMIN')")
   @RequestMapping("/roleAuth")
   public String role(){
   	return "hello";
   }
//  2.2 在主启动类上面添加这个注解,作用是开启 @PreAuthorize效果
@EnableGlobalMethodSecurity(prePostEnabled = true)
// 2.3 在SpringSecurityConfig.class 类方法中增加一个新的用户
@Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.inMemoryAuthentication()
              // 这样,页面提交时候,密码以明文的方式进行匹配。
              // todo : 需要添加该passwordEncoder()
              .passwordEncoder(new MyPasswordEncoder())
              // 指定用户
              .withUser("admin")
              // 指定用户密码
              .password("123456")
              // 指定角色
              .roles("ADMIN");
   // 新增用户
      auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
              .withUser("demo")
              .password("demo")
              .roles("USER");
   }

2-9 SpringSecurity 的优缺点

在这里插入图片描述
在这里插入图片描述

3. Apache shiro

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3-7 用户权限认证

package com.mall.demo2.model;

import com.mall.demo2.service.UserService;
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.springframework.beans.factory.annotation.Autowired;

import java.util.*;

public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     * 作为授权来用
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 从session中获取用户信息
        User user = (User) principalCollection.fromRealm(this.getClass().getName()).iterator().next();
        // 创建空的权限集合信息
        Set<String> permissionNameList = new HashSet<>();
        // 获取当前所有的角色信息
        Set<Role> roles = user.getRoles();
        // 遍历,如果角色不为空
        if (Objects.nonNull(roles)) {
            for (Role role : roles) {
                Set<Permission> permissions = role.getPermissions();
                // 判断permissions是否为空
                if (Objects.nonNull(permissions)) {
                    for (Permission permission : permissions) {
                        // 判断一个用户是否有权限
                        permissionNameList.add(permission.getName());
                    }
                }
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permissionNameList);
        return info;
    }

    /**
     * 认证登录使用
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        User user = userService.findUserByUsername(username);
        return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());
    }
}

创建密码校验规则

package com.mall.demo2;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

/**
 * 验证密码是否匹配
 */
public class CredentialMatcher extends SimpleCredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UsernamePasswordToken passwordToken = (UsernamePasswordToken) token;
        // 取出UsernamePasswordToken中的密码
        String password = new String(passwordToken.getPassword());
        // 取出数据库中的密码
        String dbPassword = (String) info.getCredentials();
        // 判断是否相同
        return this.equals(password,dbPassword);
    }
}

定义shiro的配置类(核心配置)

package com.mall.demo2;

import com.mall.demo2.model.AuthRealm;
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.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

@Configuration
public class ShiroConfiguration {

    /**
     * 当项目启动时,shiroFilter首先会被初始化,会分别传入manager来进行构造
     * @param securityManager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean factoryBean(@Qualifier("manager") SecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        // 设置登录的url
        bean.setLoginUrl("/login");
        // 登录跳转的url
        bean.setSuccessUrl("/index");
        // 没权限访问时跳转的url
        bean.setUnauthorizedUrl("/unauthorized");
        // 定义核心拦截请求
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/index","authc");
        filterChainDefinitionMap.put("/login","anon");
        // 设置
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 返回信息
        return bean;
    }

    /**
     * @param realm
     * @return
     * @Qualifier("authRealm") 使用Spring上下文中的authRealm
     */
    @Bean("manager")
    public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm realm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(realm);
        return manager;
    }

    /**
     * 自定义authRealm
     *
     * @param matcher
     * @return
     */
    @Bean("authRealm")
    public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher) {
        // 创建AuthRealm实例
        AuthRealm authRealm = new AuthRealm();
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }

    ;

    @Bean("credentialMatcher")
    public CredentialMatcher credentialMatcher() {
        return new CredentialMatcher();
    }

    // 配置shiro和Spring关联的几个类
    /**
     *Spring 对 shiro进行处理时,使用的securityManager就是自定义的
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor attributeSourceAdvisor(@Qualifier("manager") SecurityManager securityManager){
        // 创建该实例信息
        AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        // 设置
        attributeSourceAdvisor.setSecurityManager(securityManager);
        return attributeSourceAdvisor;
    }


    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

}

package com.mall.demo2.controller;

import com.mall.demo2.model.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpSession;

@Controller
public class TestController {

  /**
   * 登录接口
   *
   * @return
   */
  @RequestMapping("/login")
  public String login() {
      return "login";
  }

  /**
   * 返回登录页面
   *
   * @return
   */
  @RequestMapping("/index")
  public String index() {
      return "index";
  }

  /**
   * 登录处理接口
   *
   * @return
   */
  @RequestMapping("/loginUser")
  public String loginUser(@RequestParam("username") String username,
                          @RequestParam("password") String password,
                          HttpSession session) {

      // 组成UsernamePasswordToken实例
      UsernamePasswordToken token = new UsernamePasswordToken(username,password);
      // shiro 认证逻辑
      Subject subject = SecurityUtils.getSubject();
      try {
          // 调用authRealm进行认证
          subject.login(token);
          // 获取登录用户主体
          User user = (User)subject.getPrincipal();
          // 将用户存入session
          session.setAttribute("user",user);
          // 成功返回Index页面
          return "index";
      }catch (Exception e){
          return "login";
      }
  }
}

在application.properties中加入MVC拦截器

## jsp ##
spring.mvc.view.prefix=/pages/
spring.mvc.view.suffix=.jsp

UserMapper.xml

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.mall.demo2.mapper.UserMapper">
    <resultMap id="userMap" type="com.mall.demo2.model.User">
        <id property="uid" column="uid"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <collection property="roles" ofType="com.mall.demo2.model.Role">
            <id property="rid" column="rid"/>
            <result property="name" column="rname"/>
            <collection property="permissions" ofType="com.mall.demo2.model.Permission">
                <id property="pid" column="pid"/>
                <result property="name" column="name"/>
                <result property="url" column="url"/>
            </collection>
        </collection>
    </resultMap>

    <select id="findUserByUsername" resultMap="userMap" parameterType="java.lang.String">
        select u.*,r.*,p.*
        from `user` u
        inner join user_role ur on ur.uid = u.uid
        inner join role r on r.rid = ur.rid
        inner join permission_role rp on rp.rid = r.rid
        inner join permission p on p.pid = rp.pid
        where u.username = #{username}
    </select>
</mapper>

在TestController中添加方法,用于实现case1 ,某个用户拥有访问权限,去访问其他资源


    /**
     * 退出登录
     *
     * @return
     */
    @RequestMapping("/logout")
    public String logout() {
        // 取出验证主体
        Subject subject = SecurityUtils.getSubject();
        if (Objects.nonNull(subject)){
            subject.logout();
        }
        return "login";
    }


    @RequestMapping("/admin")
    @ResponseBody
    public String admin(){
        return "admin success";
    }

在ShiroConfig 方法中添加信息

/**
     * 当项目启动时,shiroFilter首先会被初始化,会分别传入manager来进行构造
     * @param securityManager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean factoryBean(@Qualifier("manager") SecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        // 设置登录的url
        bean.setLoginUrl("/login");
        // 登录跳转的url
        bean.setSuccessUrl("/index");
        // 没权限访问时跳转的url
        bean.setUnauthorizedUrl("/unauthorized");
        // 定义核心拦截请求
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/index","authc");
        filterChainDefinitionMap.put("/login","anon");
        // 此接口不需要做身份认证
        filterChainDefinitionMap.put("/loginUser","anon");
        // 拦截其他接口
        filterChainDefinitionMap.put("/**","user");

        // 设置
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 返回信息
        return bean;
    }

// 定义不同角色可以访问不同的接口
在ShiroConfiguration类中添加

 /**
     * 当项目启动时,shiroFilter首先会被初始化,会分别传入manager来进行构造
     * @param securityManager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean factoryBean(@Qualifier("manager") SecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        // 设置登录的url
        bean.setLoginUrl("/login");
        // 登录跳转的url
        bean.setSuccessUrl("/index");
        // 没权限访问时跳转的url
        bean.setUnauthorizedUrl("/unauthorized");
        // 定义核心拦截请求
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/index","authc");
        filterChainDefinitionMap.put("/login","anon");
        // 此接口不需要做身份认证
        filterChainDefinitionMap.put("/loginUser","anon");
        // 定义不同角色可以访问不同的接口
        filterChainDefinitionMap.put("/admin","roles[admin]");
        // 拦截其他接口
        filterChainDefinitionMap.put("/**","user");

        // 设置
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 返回信息
        return bean;
    }

在AuthRealm类中添加 角色名称集合


    /**
     * 作为授权来用
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 从session中获取用户信息
        User user = (User) principalCollection.fromRealm(this.getClass().getName()).iterator().next();
        // 创建空的权限集合信息
        Set<String> permissionNameList = new HashSet<>();
        // 创建角色名称集合
        Set<String> roleNameList = new HashSet<>();
        // 获取当前所有的角色信息
        Set<Role> roles = user.getRoles();
        // 遍历,如果角色不为空
        if (Objects.nonNull(roles)) {
            for (Role role : roles) {
                roleNameList.add(role.getRname());
                Set<Permission> permissions = role.getPermissions();
                // 判断permissions是否为空
                if (Objects.nonNull(permissions)) {
                    for (Permission permission : permissions) {
                        // 判断一个用户是否有权限
                        permissionNameList.add(permission.getName());
                    }
                }
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permissionNameList);
        // 设置角色名称
        info.setRoles(roleNameList);
        return info;
    }

3-8 实现缓存

在ShiroConfiguration 中添加

  /**
     * 自定义authRealm
     *
     * @param matcher
     * @return
     */
    @Bean("authRealm")
    public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher) {
        // 创建AuthRealm实例
        AuthRealm authRealm = new AuthRealm();
        // 添加缓存到catch中
        authRealm.setCacheManager(new MemoryConstrainedCacheManager());
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }

在这里插入图片描述

4. 权限管理系统设计

4.1 开发管理系统

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4-2 详细表设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值