美文网首页
【Shiro】一步步的看Shiro (一)

【Shiro】一步步的看Shiro (一)

作者: 程序员佩奇 | 来源:发表于2021-07-06 15:47 被阅读0次
    • shiro可以应用于任何应用
    • 简单的例子我们先把shiro跑起来
    • 这个实例需要jdk1.6或更高版本,maven在2.2.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 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.apache.shiro.tutorials</groupId>
        <artifactId>shiro-tutorial</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <name>First Apache Shiro Application</name>
        <packaging>jar</packaging>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                        <encoding>${project.build.sourceEncoding}</encoding>
                    </configuration>
                </plugin>
            <!-- 运行main函数java代码用的,如果用idea可以不考虑 -->
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>exec-maven-plugin</artifactId>
                    <version>1.1</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>java</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <classpathScope>test</classpathScope>
                        <mainClass>Tutorial</mainClass>
                    </configuration>
                </plugin>
            </plugins>
        </build>
        <dependencies>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.4.1</version>
            </dependency>
            <!-- slf4j日志包 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.7.21</version>
                <scope>test</scope>
            </dependency>
           <!-- 桥接包来代替commons-logging把具体实现委托给slf4j -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.21</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>
    
    • 来创建个启动类,在src/main/java/Tutorial.java
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public class Tutorial {
        private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
        public static void main(String[] args) {
            log.info("My First Apache Shiro Application");
            System.exit(0);
        }
    }
    
    • 执行一下main方法就好咯

    上面只是搭建一个很简单的应用,下面就具体说说需要配置写什么

    SecurityManager

    • shiro的核心管理器,在Shiro里面几乎所有的东西和SecurityManager有关系,每一个Shiro应用中必然存在一个SecurityManager,所以我们必须干一件事情就是设置SecurityManager

    Configuration

    • 我们可以实例话SecurityManager,而且shiro为我们启动SecurityManager提供了很多的配置项,看一下
    • 下面就先用shiro.ini为例:

    src/main/resources/shiro.ini

    # =============================================================================
    # Tutorial INI configuration
    #
    # Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
    # =============================================================================
    # -----------------------------------------------------------------------------
    # [users] 用来配置用户以及密码角色的,后面可以通过数据库来配置,这里只是演示
    # 配置方法:username = password, role1, role2, ..., roleN
    # -----------------------------------------------------------------------------
    [users]
    root = secret, admin
    guest = guest, guest
    presidentskroob = 12345, president
    darkhelmet = ludicrousspeed, darklord, schwartz
    lonestarr = vespa, goodguy, schwartz
    # -----------------------------------------------------------------------------
    # roles是配置角色的
    # 配置方法:roleName = perm1, perm2, ..., permN
    # -----------------------------------------------------------------------------
    [roles]
    admin = *
    schwartz = lightsaber:*
    goodguy = winnebago:drive:eagle5
    
    • 配置好了以后我们就可以实例话SecurityManager了
    public static void main(String[] args) {
        log.info("My First Apache Shiro Application");
        //1.IniSecurityManagerFactory通过工厂模式的方法读取classpath路径下的shiro.ini文件,得到一个工厂,
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //2.通过工厂获取到SecurityManager的实例对象
        SecurityManager securityManager = factory.getInstance();
        //3.这里是设置到SecurityUtils的静态单利当中,JVM可以直接访问,如果说使用spring或者其他复杂的应用,会通过框架的方式放在特定的内存中进行维护(例如在Web应用程序的ServletContext或Spring)
        SecurityUtils.setSecurityManager(securityManager);
        System.exit(0);
    }
    

    Subject

    • 在上面实例中,我们可能需要直到谁是用户?用什么表示当前用户?,当前用户是否允许执行某某操作?,所以会出现Subject的概念
    • 为什么要用Subject呢?而不用User?官方中说的意思是User代表的是人类,但是在应用程序中,Subject可以看作一个人,也可以是第三方进程,守护进程账户类似的东西进行形容
    Subject currentUser = SecurityUtils.getSubject();
    //我们可以通过SecurityUtils.getSubject()的方式获取当前用户,当用户还没有登陆的时候则是微登陆状态匿名的,官方解释;当前执行用户的安全特定视图,它基于与当前线程或传入请求相关联的用户数据获取对象。
    
    

    那么我们有了上面的用户主题,可以用来做些什么呢?看下面。

    session

    • session是shiro维护的一个特殊的实例,可以把他看作HttpSession,shiro的session会额外增加一些好东西,更重要的特点是可以不依赖于http环境,独立存在
    • 在web环境下是基于httpSession的,但是在非web环境下也可以使用session
      说完了Subject和session,那么我们如何去校验当前用户是否可以执行某一操作?如何检查他们的权限,角色?
      首先我们需要为已知当前登陆的用户进行权限校验,我们都知道Subject代表当前的用户,但是谁?是当前的用户呢?,好吧,他们是匿名的,直到他们登陆了一次以后,我们可以获取到当前登陆的用户。
    if ( !currentUser.isAuthenticated() ) {
        //我们用GUI特定的方式收集principals(主体,用户) and credentials(凭据,密码)
        //比如form表单提交的用户名密码,或者OpenId等
        //这里使用的是用户名密码
        UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
        //内置的,设置登陆后记住我
        token.setRememberMe(true);
        currentUser.login(token);
    }
    
    • 上面其实就是进行了一个登陆的操作,那么登陆遇到错误怎么办呢?
    try {
        currentUser.login( token );
        //正常登陆
    } catch ( UnknownAccountException uae ) {
        //用户不存在的异常
    } catch ( IncorrectCredentialsException ice ) {
        //密码不匹配,是否在进行重试呢?
    } catch ( LockedAccountException lae ) {
        //用户被锁定了,是不是需要给告知用户被锁定呢?
    }
        ... more types exceptions to check if you want ...
    } catch ( AuthenticationException ae ) {
        //意外情况
    }
    
    • 如果shiro提供的异常不满足你,也可以抛出自己自定义的异常
    • 好了,我们有了一个登陆的用户以后,可以做些什么呢?
    • 下面就是官方给出的一个完成例子:
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public class Tutorial {
        private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
        public static void main(String[] args) {
            log.info("My First Apache Shiro Application");
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            // 获取当前用户,在没登陆的情况下是匿名的
            Subject currentUser = SecurityUtils.getSubject();
            // 获取shiro的session实例
            Session session = currentUser.getSession();
            session.setAttribute("someKey", "aValue");
            String value = (String) session.getAttribute("someKey");
            if (value.equals("aValue")) {
                log.info("Retrieved the correct value! [" + value + "]");
            }
            // 校验是否认证过了
            if (!currentUser.isAuthenticated()) {
                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 more exceptions here (maybe custom ones specific to your application?
                catch (AuthenticationException ae) {
                    //unexpected condition?  error?
                }
            }
            //获取当前登录用户信息
            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.");
            }
            //还可以校验具体的某个功能的权限
            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);
        }
    }
    

    总结

    • 上面只是简单的初步介绍了一下shiro的使用,说了几个核心的概念,SecurityManager,session,Subject
    • 如果不想使用shiro.ini配置文件怎么办呢,我们在实际开发中确实也很少使用这种方式,那么我们肯定需要和数据库打交道
    • 想解决shiro.ini不想用的问题,下面就需要继续学习一下关于Shiro架构和他所支持的配置相关的内容了

    相关文章

      网友评论

          本文标题:【Shiro】一步步的看Shiro (一)

          本文链接:https://www.haomeiwen.com/subject/hlorzktx.html