shiro学习01-安全框架相关名词与shiro常用类粗略介绍

安全框架其实就干了两件事,一是用户能不能登录,第二个是登录之后能做什么,就这些,shiro的作用也就这么多,所以在学习要对shiro采取一种心理上的藐视,因为他所有的工作都是为了满足这两个来实现的。

一、shiro相关的名词解释,因为后面的文档都是用的专有名词,所以我们在这里先将这些名词说明一下,以免造成误解。

1、Authentication  校验,验证;证明一个主题身份的过程,比我我们在用用户名+密码登录网站时就是验证的过程,验证我就是这个用户,因为我知道他的密码和用户名(说白了,就是能不能登录)。

2、Authorization(Access Control):授权,检查某个用户有没有权利做哪一件事,通常通过用户的权限完成(说白了就是指定用户能做什么事)。

3、Cipher,加密,这个不用多想,就是将我们的普通密码加密,比如用MD5算法

4、Credential,一个能够标示一个用户或者是主题的信息,就是我们的密码。

5、Cryptography,这个包括Cipher,也就是加密还包括格式化显示,比如hash,base64.

6、Hash,一种算法,讲一个输入信息转换为编码后的信息,并且是不可逆的。

7、Permission,英文翻译是许可,这里翻译为权限,表示能做什么事,与subject无关。

8、Principal,主题,就是我们平时用的username或者是id,比如qq号。

9、Real,这个是用来存储我们定义好的用户、角色以及权限的地方,可以理解为一个和安全相关的dao。

10、Role,就是角色,我们在开发中经常用到用户-->角色-->权限,角色就是这里的Role。其中一个用户有多个角色,一个角色对应多个权限。

11、Session,类似于我们的HttpSession,但是shiro的session功能更强悍。

12、subject,就是用户,user。只不过这里的user并不一定是一个人,而是任何可能会访问我们应用的请求,比如定期调度的工作。

二、 shiro的主要接口的大致介绍,先提前说明一下,shiro的类和类之间是相互依赖的,有的意思我可能表达不准确,不要着急,在以后的更细致的章节中我会有更深入的讲解。写这个类的目的是为了方便查找的,方便你,也是方便我。

0、SecurityManager,这个类是shiro的核心,shiro的所有功能(登录校验、授权)都是通过这个类实现的,。但是他其实什么都不做,他只是一个平台,就像淘宝一样,自己啥也不卖,而是让别人卖 。securityManager是通过它内部的属性来实现的各种功能的,其中最主要的就是Reaml。

1、Real,realm有“域”的意思,引申为“来源”,也就是说某些东西原来是在哪的,在shiro判断某个用户能不能登录或者是能不能做什么事的时候,他需要知道之前程序对该用户的信息(比如用户名,密码,角色,权限)的存储放在哪里,这个“哪里”就是realm,我们在实际开发中只有一个域,那就是关系型数据库中的User表,但是有的情况下可能有多个域,比如某些网站允许你使用qq号登陆,也允许你使用自己注册的账号登陆,那就有两个域。简单一句话,域就是存储用户信息的地方,这个很好理解。但是为了方便大家在后面的文章中理解,我们这里假设我们只有一个域,而且这个域就是我们自己定义的数据库中的user表,请先记住我们的这一假设。在以后shiro的登录校验和权限管理的时候都是需要这个接口的,因为他存储了所有的信息。他的实现类有AuthenticationRealm,用来处理登录,AuthorizingRealm,用来处理访问权限。

2、AuthenticationToken,校验的标签,这个类使用在用户登录的时候,用户填写了用户名+密码,然后我们将其封装在这个类中,用来和数据库中的比较,然后得出结果,返回的结果就是下一个类。

3、AuthenticationInfo,校验的信息,这个类是在校验完了用户登录之后产生的,校验的结果有很多种,比如根本不存在这个用户或者有用户但是密码不对或者当前用于由于多次输入错误密码而被锁定或者当前用户没有激活(有的情况下可能需要用邮箱激活)或者是重复登录等等,完全可以定义自己的校验失败的情况。在校验成功之后,我们就要返回一个AuthenticationInfo,里面包含了这个用户的很多标示符Principal(不仅仅是用户名)和密码(Credential)。

4、AuthenticationException 用户登录校验失败在shiro中用异常来表示,这个类就是最大的校验失败异常的父类。在shiro中,和很多他的子类,都有:

     4.1 ConcurrentAccessException   重复登录

     4.2LockedAccountException  用户被锁定

     4.3UnknownAccountException  不存在的用户

     4.4IncorrectCredentialsException  密码不正确

    我们可以继承这个类来定义自己的异常。

5、PrincipalCollection,从字面意思上看是“标识符集合”,也就是一个用户的所有的标识符的意思。这个是AuthenticationInfo的一个属性,在校验完用户之后,因为可能有多个realm,所以就可能有多个realm,所以就有了要将多个realm和域对应起来的要求,在这个类中有一个fromReaml方法就是返回指定realm的principal。这里还涉及到一个primary principal的概念,表示在这个系统中可以唯一表示某个用户的信息,比如user表的id。

-----------------------------------看完上面这些就可以看下一节了(用户登录校验),下面的是关于访问权限的接口-------------------------------

6、AuthorizationInfo,这个可以类比于AuthenticationInfo,AuthenticationInfo是对根据传入的AuthenticationToken从数据库中或者用户的用户名和密码的封装,那么AuthorizationInfo就是对从数据库中获得的用户的角色和权限的封装。

7、Permission,表示用户的权限的类。

8、PermissionResolver 表示将存储的字符串转变为Permission的类。

9、RolePermissionResolver,这个类的作用和上面的一样,用来将字符串转变为权限,但是这个存在的情况是在当AuthorizingInfo返回的是一些角色的时候,将角色转化为Permission。

10AuthorizingRealm 这个是负责访问权限的类,它里面有很多的方法,6-9的所有类都是在这个里面起作用的。

这就是shiro的最常用的类,当然还有一些类,比如securityUtil我没有写,那是因为我将在最后面介绍这些类,先绕过他反而对我们的学习有益处。在进行下一节之前先看看官网上给出的quick start.

shiro官方给出了quick start,方便大家快速入门,这里我们一起看看,顺便将官方的解释翻译出来。下载shiro解压之后,进入到shiro-root-1.2.4\samples\quickstart\src\main\java这个路径下,就能发现这个java文件,如下:

public class Quickstart {
    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    public static void main(String[] args) {
        // The easiest way to create a Shiro SecurityManager with configured
        // realms, users, roles and permissions is to use the simple INI config.
        // We'll do that by using a factory that can ingest a .ini file and
        // return a SecurityManager instance:

        // Use the shiro.ini file at the root of the classpath
        // (file: and url: prefixes load from files and urls respectively):
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();

        // for this simple example quickstart, make the SecurityManager
        // accessible as a JVM singleton.  Most applications wouldn't do this
        // and instead rely on their container configuration or web.xml for
        // webapps.  That is outside the scope of this simple quickstart, so
        // we'll just do the bare minimum so you can continue to get a feel
        // for things.
        SecurityUtils.setSecurityManager(securityManager);

        // Now that a simple Shiro environment is set up, let's see what you can do:

        // get the currently executing user:
        Subject currentUser = SecurityUtils.getSubject();

        // Do some stuff with a Session (no need for a web or EJB container!!!)
        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 + "]");
        }

        // let's login the current user so we can check against roles and permissions:
        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?
            }
        }

        //say who they are:
        //print their identifying principal (in this case, a username):
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        //test a role:
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        //test a typed permission (not instance-level)
        if (currentUser.isPermitted("lightsaber:weild")) {
            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!");
        }

        //all done - log out!
        currentUser.logout();

        System.exit(0);
    }
}

下面是摘取的官网上对于上面java类的解释:

Subject currentUser = SecurityUtil.getSubject();

 使用这个语句可以得到当前正在执行的Subject,一个Subject就是一个我们平时说的用户,当调用这个方法时,如果我们是在一个web环境时,会从当前线程上获取用户,毫无疑问这里使用了ThreadLocal模式。

Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );

可以通过调用getSession方法获得Session并往session中设置值,这里的session是与httpSeesion没有关系,在非web环境下也可以使用。

if ( !currentUser.isAuthenticated() ) {
    //collect user principals and credentials in a gui specific manner 
    //such as username/password html form, X509 certificate, OpenID, etc.
    //We'll use the username/password example here since it is the most common.
    //(do you know what movie this is from? ;)
    UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
    //this is all you have to do to support 'remember me' (no config - built in!):
    token.setRememberMe(true);
    currentUser.login(token);
}

上面的一段代码是校验用户身份的代码,用户的身份体现在userNamePasswordToken中,如果我们是做的用户登录模块的话,就可以使用页面上传来的username和pasword构建此代码,然后调用currentUser.login()方法,如果和数据库(这里只是假设用户的信息存在数据库中)中的信息一致,则校验成功,否则会抛异常。

try {
    currentUser.login( token );
    //if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
    //username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {
    //password didn't match, try again?
} catch ( LockedAccountException lae ) {
    //account for that username is locked - can't login.  Show them a message?
} 
    ... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
    //unexpected condition - error?
}

上面的代码是捕获在调用login方法时可能抛出的异常。可能出现的异常都有:用户不可知、密码不正确、用户被锁定,shiro允许我们定义自己的异常

if ( currentUser.hasRole( "schwartz" ) ) {
    log.info("May the Schwartz be with you!" );
} else {
    log.info( "Hello, mere mortal." );
}

判断角色,用hasRole方法,

if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
    log.info("You may use a lightsaber ring.  Use it wisely.");
} else {
    log.info("Sorry, lightsaber rings are for schwartz masters only.");
}

有没有某个权限,先不用管"lightsaber:weild"的意思,知道他是一个权限即可。

从这里看shiro很简单,但是有一个很深的疑问,login方法中根据username  password判断用户,那么这些username password在什么地方定义呢,又是怎么取出来的呢,同样的疑问也在权限和角色上。不急,慢慢就懂了。

相关推荐