• SpringBoot集成Shiro安全框架


    SpringBoot集成Shiro安全框架

    1.shiro的定义

    1.shiro的作用
    认证、授权、加密、会话管理、Web集成、缓存

    2.shiro的名词
    在这里插入图片描述

    Authentication:身份认证/登录,验证用户是不是拥有相应的身份
    Authorization:授权,即权限 管理,验证某个人已经登录的人是否拥有某些权限
    Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前所有的信息都是在会话中。。
    Cryptography:加密,保护数据的安全性,密码加密
    Web Support:Web支持,可以非常容易的集成到web环境
    Caching:缓存,比如用户登录之后,他的用户信息,权限不必每次去查
    Concurrency:shiro支持多线程应用兵法验证,即如果在一个线程中开启另外一个线程,权限会自动传递过去
    Testing:提供测试支持
    Run AS:允许一个用户假装另一个用户(如果他们允许)的身份进行访问
    Remember Me:记住我,这是一个常见的功能,即一次登录后,下次再来的话就不用登录了
    记住一点,Shiro不会去维护用户、维护权限:这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro即可!

    3.shiro的架构
    在这里插入图片描述
    Subject,Subject其实代表的就是当前正在执行操作的用户,只不过因为“User”一般指代人,但是一个“Subject”可以是人,也可以是任何的第三方系统,服务账号等任何其他正在和当前系统交互的第三方软件系统。
    所有的Subject实例都被绑定到一个SecurityManager,如果你和一个Subject交互,所有的交互动作都会被转换成Subject与SecurityManager的交互。

    SecurityManager。SecurityManager是Shiro的核心,他主要用于协调Shiro内部各种安全组件,不过我们一般不用太关心SecurityManager,对于应用程序开发者来说,主要还是使用Subject的API来处理各种安全验证逻辑。

    Realm,这是用于连接Shiro和客户系统的用户数据的桥梁。一旦Shiro真正需要访问各种安全相关的数据(比如使用用户账户来做用户身份验证以及权限验证)时,他总是通过调用系统配置的各种Realm来读取数据。
    在这里插入图片描述

    2.SpringBoot集成shiro的步骤

    1.添加maven依赖

     
            
                org.apache.shiro
                shiro-core
                1.4.0
            
            
                org.apache.shiro
                shiro-web
                1.4.0
            
            
                org.apache.shiro
                shiro-ehcache
                1.4.0
            
            
                org.apache.shiro
                shiro-spring
                1.4.0
            
            
            
                com.github.theborakompanioni
                thymeleaf-extras-shiro
                2.0.0
            
            
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2.新建缓存文件
    ehcache-shiro.xml

    
    
        
    
        
    
        
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    3.重要的shiro配置类
    不需要完全记住,只需要修改其中的一小部分

    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    import org.apache.shiro.cache.ehcache.EhCacheManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
    import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.CookieRememberMeManager;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * @ Author     :Zgq
     * @ Date       :Created in 18:22 2019/6/11
     * @ Description:shiro的配置类
     * @ Modified By:
     * @Version: $
     */
    @Configuration
    public class ShiroConfig {
    
        /**
        唯一需要修改的地方,有注释
         * ShiroFilterFactoryBean 处理拦截资源文件问题。
         * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,
         * 因为在初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
         * Filter Chain定义说明
         * 1、一个URL可以配置多个Filter,使用逗号分隔
         * 2、当设置多个过滤器时,全部验证通过,才视为通过
         * 3、部分过滤器可指定参数,如perms,roles
         */
        @Bean
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 必须设置 SecurityManager
            shiroFilterFactoryBean.setSecurityManager(securityManager);
    
            // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
            //访问的是后端url地址为 /login的接口,/未登录页面,会跳转到登录页面
            shiroFilterFactoryBean.setLoginUrl("/login");
            // 登录成功后要跳转的链接
            shiroFilterFactoryBean.setSuccessUrl("/index");
            // 未授权界面;,基于AOP拦截,都会到登录页面
            shiroFilterFactoryBean.setUnauthorizedUrl("/login");
            // 拦截器.
            Map filterChainDefinitionMap = new LinkedHashMap();
    
            // 配置不会被拦截的链接 顺序判断,anon放开,不会拦截,authc会拦截
            //静态资源不能被拦截
            filterChainDefinitionMap.put("/assets/**", "anon");
            filterChainDefinitionMap.put("/css/**", "anon");
            filterChainDefinitionMap.put("/js/**", "anon");
            filterChainDefinitionMap.put("/images/**", "anon");
            filterChainDefinitionMap.put("/fonts/**", "anon");
    
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/userLogin", "anon");
            // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
            filterChainDefinitionMap.put("/logout", "logout");
            //配置某个url需要某个权限码
            filterChainDefinitionMap.put("/hello", "perms[how_are_you]");
            // 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
            // 
           filterChainDefinitionMap.put("/**", "authc");
    
            //配置记住我或认证通过可以访问的地址
            filterChainDefinitionMap.put("/index", "user");
    //        filterChainDefinitionMap.put("/", "user");
    
    
    
            System.out.println("Shiro拦截器工厂类注入成功");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
    
        /**
         * 缓存
         * @return
         */
        @Bean
        public EhCacheManager getEhCacheCache() {
            EhCacheManager em = new EhCacheManager();
            em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
            return em;
        }
    
    
        /**
         * 代理
         * @return
         */
        @Bean
        public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
            daap.setProxyTargetClass(true);
            return daap;
        }
    
    
        @Bean
        public DefaultWebSessionManager getDefaultWebSessionManager() {
            DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
            defaultWebSessionManager.setSessionDAO(getMemorySessionDAO());
            defaultWebSessionManager.setGlobalSessionTimeout(1 * 60 * 60 * 1000);
            defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
            defaultWebSessionManager.setSessionIdCookieEnabled(true);
            defaultWebSessionManager.setSessionIdCookie(getSimpleCookie());
            return defaultWebSessionManager;
        }
    
    
        @Bean
        public MemorySessionDAO getMemorySessionDAO() {
            MemorySessionDAO memorySessionDAO = new MemorySessionDAO();
            memorySessionDAO.setSessionIdGenerator(javaUuidSessionIdGenerator());
            return memorySessionDAO;
        }
    
    
        @Bean
        public JavaUuidSessionIdGenerator javaUuidSessionIdGenerator() {
            return new JavaUuidSessionIdGenerator();
        }
    
        /**
         * cookie对象
         * @return
         */
        @Bean
        public SimpleCookie getSimpleCookie() {
            SimpleCookie simpleCookie = new SimpleCookie();
            simpleCookie.setName("security.session.id");
            //
            simpleCookie.setMaxAge(259200);
            simpleCookie.setPath("/");
            return simpleCookie;
        }
    
        /**
         * cookie管理对象;
         * @return
         */
        /*@Bean
        public CookieRememberMeManager rememberMeManager(){
            System.out.println("ShiroConfiguration.rememberMeManager()");
            CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
            cookieRememberMeManager.setCookie(getSimpleCookie());
            return cookieRememberMeManager;
        }*/
    
        @Bean
        public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
    
        /**
         * 注入 securityManager
         */
        @Bean(name = "securityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(
                ShiroRealm shiroRealm) {
            DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
            // 关联realm.
            dwsm.setRealm(shiroRealm);
            //用户授权/认证信息Cache,采用EhCache缓存
            dwsm.setCacheManager(getEhCacheCache());
            dwsm.setSessionManager(getDefaultWebSessionManager());
            //注入记住我管理器;
          /*  dwsm.setRememberMeManager(rememberMeManager());*/
            return dwsm;
        }
    
        @Bean
        public ShiroRealm shiroRealm(EhCacheManager cacheManager) {
            ShiroRealm shiroRealm = new ShiroRealm();
            shiroRealm.setCacheManager(cacheManager);
            return shiroRealm;
        }
    
        //开启shiro注解支持
        @Bean
        public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(ShiroRealm shiroRealm){
            AuthorizationAttributeSourceAdvisor aasa =new AuthorizationAttributeSourceAdvisor();
            aasa.setSecurityManager(getDefaultWebSecurityManager(shiroRealm));
            return aasa;
        }
    
        /**
         * 配置前台页面thymeleaf页面的标签
         * @return
         */
        @Bean
        public ShiroDialect shiroDialect() {
            return new ShiroDialect();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207

    4.核心的授权认证类

    import com.example.echart.entity.Permission;
    import com.example.echart.entity.Role;
    import com.example.echart.entity.User;
    import com.example.echart.mapper.UserRoleMapper;
    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.session.Session;
    import org.apache.shiro.session.mgt.eis.SessionDAO;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.support.DefaultSubjectContext;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    /**
     * @ Author     :Zgq
     * @ Date       :Created in 18:19 2019/6/11
     * @ Description:Shiro中最主要的代码,核心代码,用户认证授权处
     * @ Modified By:
     * @Version: $
     */
    public class ShiroRealm extends AuthorizingRealm {
    
    
        @Autowired
        private SessionDAO sessionDAO;
    
        @Autowired
        private UserRoleMapper userRoleMapper;
        
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection auth) {
            //授权
            String username = (String)auth.getPrimaryPrincipal();
            System.out.println("进入到授权Realm中:"+username);
    
            List dbroleList = userRoleMapper.selectRoleList(username);
            List roleList=new ArrayList();
            for(Role r:dbroleList){
                roleList.add(r.getCode());
            }
    
            List dbpermissions = userRoleMapper.selectPermissionList(username);
            List permissionList=new ArrayList();
            for(Permission p:dbpermissions){
                permissionList.add(p.getPermission());
            }
    
    
    
    //        roleList.add("ADMIN");
    //        roleList.add("USER");
    
    //        List permissionList=new ArrayList();
    //        permissionList.add("ADMIN:USER:UPDATA");
    //        permissionList.add("ADMIN:USER:DELETE");
    
            SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.addRoles(roleList);
            simpleAuthorizationInfo.addStringPermissions(permissionList);
    
            return simpleAuthorizationInfo;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
            //认证
            String username = (String)auth.getPrincipal();
            System.out.println("进入到认证Realm中:"+username);
    
            //在认证之前判断当前登录用户,只允许一个账号登录
            Collection sessions = sessionDAO.getActiveSessions();
            for (Session session : sessions){
                String loginedUsername = String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));
    
                if(username.equals(loginedUsername)){
                    session.setTimeout(0);
                    break;
                }
            }
    
    
            //通过username在数据库中查询用户,判断密码
            User dbuser = userRoleMapper.selectByUserName(username);
    
            //通过用户名在数据库中拿到,判断用户名和密码对不对
            if(dbuser!=null){
                SimpleAuthenticationInfo authInfo = new SimpleAuthenticationInfo(username, dbuser.getPwd(), "userRealm");
                return authInfo;
            }
    
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    5.登录的Controller类,LoginController

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import java.util.Map;
    
    /**
     * @ Author     :Zgq
     * @ Date       :Created in 10:30 2019/6/12
     * @ Description:登录层方法
     * @ Modified By:
     * @Version: $
     */
    
    @Controller
    public class LoginController {
    
        //登录页面
        @RequestMapping(value = {"/login"})
        public String login(Map map) {
            map.put("msg","请登录");
            return "/shiro/login-page";
        }
    
    
        //登录成功页面
        @RequestMapping(value = {"/index"})
        public String index(Map map) {
            map.put("msg","登录成功");
            //获取用户信息
            Subject subject = SecurityUtils.getSubject();
            String username = (String)subject.getPrincipal();
            map.put("username",username);
            return "/shiro/index";
        }
    
        //登录请求
        @RequestMapping(value = {"/userLogin"})
        public String userLogin(String username, String pwd,boolean rememberMe, Map map)
        {
            Subject subject = SecurityUtils.getSubject();
    
            //根据自己盐加密的方式,放入密码
            String encodePwd = new Md5Hash(pwd, username).toString();
    
            UsernamePasswordToken auth = new UsernamePasswordToken(username, encodePwd,rememberMe);
            try {
                subject.login(auth);
                return "redirect:/index";
            }catch (Exception e){
                e.printStackTrace();
                map.put("msg","账号或密码错误");
                return "redirect:/login";
            }
        }
    
    
        //退出登录
        @RequestMapping(value = {"/loginOut"})
        public String loginOut(Map map) {
    
            //获取用户信息
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
    
            map.put("msg","退出登录");
            return "redirect:/login";
        }
    
    
        /**
         * 加密的测试
         * @param args
         */
        public static void main(String[] args) {
            //加密
            String zhouguoqing = new Md5Hash("111", "admin1").toString();
            System.out.println(zhouguoqing);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    6.因为是和数据库直接对接的,所以我也新建了一个UserRoleMapper接口,返回数据

    import com.example.echart.entity.Permission;
    import com.example.echart.entity.Role;
    import com.example.echart.entity.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    @Mapper
    @Component
    public interface UserRoleMapper {
    
        /**
         * 通过登录名查询信息
         * @param username
         * @return
         * @throws Exception
         */
        @Select("select * from t_user where username=#{username} limit 1")
        User selectByUserName(String username);
    
    
        /**
         * 通过用户名查询用户角色信息
         * @param username
         * @return
         * @throws Exception
         */
        @Select("select * from t_user u,t_role r,t_user_role ur where u.username=#{username} and ur.userId=u.id and ur.roleId=r.id")
        List  selectRoleList(String username);
    
    
        /**
         * 通过用户名查找用户权限
         * @param username
         * @return
         */
        @Select("select * from t_permission p 
    " +
                "where p.id in(
    " +
                "	select permissionId from t_role_permission rp 
    " +
                "	where rp.roleId in (
    " +
                "			select ur.roleId from t_user_role ur where userId in(
    " +
                "			select u.id from t_user u where u.username=#{username}
    " +
                "			)
    " +
                "	)
    " +
                ")")
        List selectPermissionList(String username);
    
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    7.前台页面thymeleaf的展示

    
    
    
        
        Title
    
    
    
    
    你好:
    
    

    ADMIN角色

    USER角色

    SUPERMAN角色

    UPDATA权限

    DELETE权限

    INSERT权限

    SELECT权限

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3.完成的效果

    在这里插入图片描述
    用不同用户登录之后会自动获取登录用户的角色和权限信息

    学习地址:https://www.bilibili.com/video/av44084437

  • 相关阅读:
    2023-09-07 LeetCode每日一题(修车的最少时间)
    蓝桥杯嵌入式AD采样解析
    mysql的配置文件(my.ini或者 my.cnf)所在位置
    软件接口测试为什么很重要,软件检测机构如何收费?
    Redis高级篇——Redis的优化
    Spring原生api操作之如何在spring配置文件添加Bean对象到Spring容器
    TypeScript 基础学习
    递归:解题思路
    【05】FISCOBCOS中的节点配置
    TCR历史研究夏校申请详解
  • 原文地址:https://blog.csdn.net/m0_54866636/article/details/126515976