shiro

简介

官网:http://shiro.apache.org/

功能

img

  • Authentication:身份认证,登陆,验证用户是不是拥有相应身份
  • Authorization:授权,即权限验证
  • Session Manger:会话管理,用户登陆后就是一次会话,没退出之前,所有信息都在会话中,会话可以是JavaSe环境,也可以是Web环境
  • Crytography:加密
  • Caching:缓存
  • Concurrency:多线程应用的并发验证,如在一个线程中开启另一个线程,能把权限自动的传播过去
  • Testing:测试支持
  • Run As:允许一个用户假装为另一个用户进行访问
  • Remember Me:记住我
Shiro架构(外部)

img

  • subject:应用代码直接交互的对象。代表当前用户,用户不一定是一个具体的人,是与当前应用交互的任何东西,像网络爬虫,机器人等。与subject的所有交互都委托给SecurityManager。subject是一个门面,SecurityManager才是实际执行者
  • SecurityManager:安全管理器。所有与安全相关的操作都会与SecurityManager交互,管理者所有subject,是shiro核心,负责与shiro其他组件交互,相当于SpringMVC中DispacherServlet
  • Realm:连接数据
Shiro架构(内部)

img

入门代码

  • 依赖

    	<dependencies>
            <!-- shiro -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.7.1</version>
            </dependency>
    
            <!-- slf4j -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.30</version>
            </dependency>
    
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.30</version>
            </dependency>
    
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
        </dependencies>
    
  • 配置文件

    [users]
    # user 'root' with password 'secret' and the 'admin' role
    root = secret, admin
    # user 'guest' with the password 'guest' and the 'guest' role
    guest = guest, guest
    # user 'presidentskroob' with password '12345' ("That's the same combination on
    # my luggage!!!" ;)), and role 'president'
    presidentskroob = 12345, president
    # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
    darkhelmet = ludicrousspeed, darklord, schwartz
    # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
    lonestarr = vespa, goodguy, schwartz
    
    # -----------------------------------------------------------------------------
    # Roles with assigned permissions
    #
    # Each line conforms to the format defined in the
    # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
    # -----------------------------------------------------------------------------
    [roles]
    # 'admin' role has all permissions, indicated by the wildcard '*'
    admin = *
    # The 'schwartz' role can do anything (*) with any lightsaber:
    schwartz = lightsaber:*
    # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
    # license plate 'eagle5' (instance specific id)
    goodguy = winnebago:drive:eagle5
    
  • 入门

    核心方法

    SecurityUtils.getSubject();//获取当前用户对象
    currentUser.getSession();//根据用户获取session
    currentUser.isAuthenticated();//用户是否被认证
    currentUser.getPrincipal();//获取认证
    currentUser.hasRole("schwartz");//是否有某个角色
    currentUser.isPermitted("lightsaber:wield");//是否有某个权限
    currentUser.logout();//注销
    
    public class Quickstart {
    
        private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    
        public static void main(String[] args) {
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.getInstance();
    
            SecurityUtils.setSecurityManager(securityManager);
            //获取当前用户对象
            Subject currentUser = SecurityUtils.getSubject();
            //通过当前用户拿到session
            Session session = currentUser.getSession();
            session.setAttribute("someKey", "aValue");
            String value = (String) session.getAttribute("someKey");
            if (value.equals("aValue")) {
                log.info("subject->session [" + value + "]");
            }
    
            //判断当前用户是否被认证
            if (!currentUser.isAuthenticated()) {
                //token:令牌
                UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
                //设置记住我
                token.setRememberMe(true);
                try {
                    currentUser.login(token);
                } catch (UnknownAccountException uae) {
                    log.info("There is no user with username of " + token.getPrincipal());
                } catch (IncorrectCredentialsException ice) {
                    log.info("Password for account " + token.getPrincipal() + " was incorrect!");
                } catch (LockedAccountException lae) {
                    log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                            "Please contact your administrator to unlock it.");
                }
                catch (AuthenticationException ae) {
                }
            }
    
            log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
    
            if (currentUser.hasRole("schwartz")) {
                log.info("May the Schwartz be with you!");
            } else {
                log.info("Hello, mere mortal.");
            }
    
            //粗粒度
            if (currentUser.isPermitted("lightsaber:wield")) {
                log.info("You may use a lightsaber ring.  Use it wisely.");
            } else {
                log.info("Sorry, lightsaber rings are for schwartz masters only.");
            }
    
            //细粒度
            //a (very powerful) Instance Level permission:
            if (currentUser.isPermitted("winnebago:drive:eagle5")) {
                log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                        "Here are the keys - have fun!");
            } else {
                log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
            }
    
             currentUser.logout();
    
            System.exit(0);
        }
    }
    

Springboot整合shiro

依赖
    <dependencies>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.6</version>
        </dependency>
        <!--log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>



        <!-- shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--thymeleaf-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>

        <!--shiro-thymeleaf整合-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

    </dependencies>
配置类
  • 自定义Realm

    需继承AuthorizingRealm,就是授权和认证

    public class UserRealm extends AuthorizingRealm {
        @Resource
        private UserService userService;
    
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了=》授权doGetAuthorizationInfo");
            //SimpleAuthorizationInfo
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //获取当前登陆对象
            Subject subject = SecurityUtils.getSubject();
            //获取user
            User currentUser = (User) subject.getPrincipal();
            if (currentUser.getPerms() != null) {
                //设置当前用户权限
                info.addStringPermission(currentUser.getPerms());
            }
            return info;
        }
    
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行了=》认证doGetAuthorizationInfo");
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
            User user = userService.queryUserByUserName(token.getUsername());
            if (user == null) {//没有这个用户
                return null;//UnknownAccountException
            }
    
            //登陆成功,user放入session,前台根据是否有此session,动态显示登陆功能
            Subject currentSubject = SecurityUtils.getSubject();
            Session session = currentSubject.getSession();
            session.setAttribute("loginUser",user);
            //可以加密:MD5: MD5盐值加密:Md5加密过程中添加额外的随机值
            //密码认证,shiro做,加密了
            return new SimpleAuthenticationInfo(user, user.getPassword(), "");//1.认证2.密码对象3.认证名
        }
    }
    
  • ShiroConfig

    三大核心对象:

    • ShiroFilterFactoryBean
    • DefaultWebSecurityManager
    • Realm
    @Configuration
    public class ShiroConfig {
        //1.ShiroFilterFactoryBean
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            //设置安全管理器
            bean.setSecurityManager(defaultWebSecurityManager);
            /*
                anon:无需认证就可访问
                authc:必须认证才能访问
                user:必须拥有记住我功能才能用
                perms:拥有某个资源的权限才能访问
                role:拥有某个角色权限才能访问
                    filterMap.put("/user/add", "authc");
            filterMap.put("/user/update", "authc");
            filterMap.put("/user/*", "authc");//通配
            */
            //拦截
            Map<String, String> filterMap = new LinkedHashMap<>();
            //授权
            filterMap.put("/user/add", "perms[user:add]");
            filterMap.put("/user/update", "perms[user:update]");
    
    
            bean.setFilterChainDefinitionMap(filterMap);
    
            //设置没有权限会发出的登陆请求
            bean.setLoginUrl("/toLogin");
    
            //为授权页面
            bean.setUnauthorizedUrl("/noauth");
    
            return bean;
        }
    
        //2.DefaultWebSecurityManager
        @Bean(name = "securityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //关联realm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
    
        //3.Realm,需要自定义
        @Bean
        public UserRealm userRealm() {
            return new UserRealm();
        }
    
        //整合ShiroDialect,用来整合shiro thymeleaf
        @Bean
        public ShiroDialect getShiroDialect() {
            return new ShiroDialect();
        }
    }
    
代码
  • controller

    @Controller
    public class MyController {
        @RequestMapping({"/", "/index"})
        public String toIndex(Model model) {
            model.addAttribute("msg", "hello world");
            return "index";
        }
    
        @RequestMapping("/user/add")
        public String add() {
            return "user/add";
        }
    
        @RequestMapping("/user/update")
        public String update() {
            return "user/update";
        }
    
        @RequestMapping("/toLogin")
        public String toLogin() {
            return "login";
        }
    
        @RequestMapping("/login")//登陆就会调用UserRealm中认证方法
        public String login(String username, String password, Model model) {
            //获取当前用户
            Subject subject = SecurityUtils.getSubject();
            //封装用户的登陆数据
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    
            //执行登陆方法,如果没有异常就说明OK
            try {
                subject.login(token);//登陆成功,返回首页
                return "index";
            } catch (UnknownAccountException uae) {//用户名不存在
                model.addAttribute("msg", "用户名不存在");
                return "login";
            } catch (IncorrectCredentialsException ice) {//密码错误
                model.addAttribute("msg", "密码错误");
                return "login";
            }
        }
    
        //未授权发出的请求
        @RequestMapping("/noauth")
        @ResponseBody
        public String unauthorized(){
            return "未经授权,不得访问";
        }
    }
    
  • 前端页面

    index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>首页</h1>
    
    <!--登陆了的用户不显示登陆连接-->  
    <div th:if="${session.loginUser==null}">
        <a th:href="@{/toLogin}">登陆</a>
    </div>
    
    <p th:text="${msg}"></p>
    
    <!--只有有user:add的用户才显示增加功能-->
    <div shiro:hasPermission="user:add">
        <a th:href="@{'/user/add'}">增加</a>
    </div>
    <!--只有有user:update的用户才显示update功能-->
    <div shiro:hasPermission="user:update">
        <a th:href="@{'/user/update'}">修改</a>
    </div>
    </body>
    </html>
    

    login.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>登陆</h1>
    <p th:text="${msg}" style="color: red"></p>
    <form th:action="@{'/login'}">
        <p>用户名:<input type="text" name="username"></p>
        <p>密码:<input type="text" name="password"></p>
        <p><input type="submit"></p>
    </form>
    </body>
    </html>
    

    add.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>add</h1>
    </body>
    </html>
    

    update.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>update</h1>
    </body>
    </html>
    
posted @   jpy  阅读(52)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示