本文转载自 燕归来

Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
本博文仅仅介绍怎么使用,至于底层原理的源码,待以后再来慢慢分析。

Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。Shiro可以帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存等。这不就是我们想要的嘛,而且Shiro的API也是非常简单;其基本功能点如下图所示:
d59f6d02-1f45-3285-8983-4ea5f18111d5
下面开始一步一步实现Shiro和Spring的继承,项目使用Maven搭建.

POM文件
shiro需要的jar包如下,导入即可

    <!--shiro核心包-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.2.2</version>
    </dependency>

    <!--shiroweb包-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-web</artifactId>
        <version>1.2.2</version>
    </dependency>

    <!--spring集成shiro包-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.2.2</version>
    </dependency>

    <!--shiro缓存配置-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-ehcache</artifactId>
        <version>1.2.2</version>
    </dependency>

    <!--缓存支持包-->
    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache-core</artifactId>
        <version>2.5.0</version>
    </dependency>

    <!--缓存规范接口-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-jdk14</artifactId>
        <version>1.5.6</version>
    </dependency>

创建配置文件
在类目录下创建spring-shiro.xml配置文件,内容如下,具体配置信息,参考注释.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd
                        http://www.springframework.org/schema/aop
                        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/mvc">

    <!--配置securityManager-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--配置缓存管理器-->
        <property name="cacheManager" ref="cacheManager"/>
        <!--配置Reamle-->
        <property name="realm" ref="customReamle"/>
    </bean>

    <!--配置缓存管理器-->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!--缓存管理的配置文件放在类目录下,其配置内容在后文-->
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
    </bean>

    <!--配置Reamle-->
    <bean id="customReamle" class="com.tao.spring.shiro.CustomReamle">
        <!--Reamle域的加密配置-->
        <property name="credentialsMatcher">
            <!--创建一个Bean -->
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--指定的加密方式-->
                <property name="hashAlgorithmName" value="MD5"/>
                <!--循环加密的次数 1024-->
                <property name="hashIterations" value="1024"/>
            </bean>
        </property>
    </bean>

    <!--其可以自定的调用Spring IOC容器中Shiro bean的生命周期方法-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!--启用IOC容器中的shiro注解-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"></bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"></property>
    </bean>

    <!--配置Shiro-->
    <!--此Bean的id必须和web.xml文件中的shiroFilter过滤器的名称一致,否则会抛出异常-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--设置登陆界面-->
        <property name="loginUrl" value="/shiro/toLoginPage"/>

        <!--配置详细信息,支持通配符-->
        <!--  anon 表示页面不需要登录即可访问-->
        <!--  authc 表示页面需要认证才可以访问-->
        <!--logout 表示注销认证状态-->
        <property name="filterChainDefinitions">
            <value>
                /login/toLoginPage = anon
                /login/toSuccessPage = authc

                /api/login = anon
                /api/logout = logout

                /shiro/toLoginPage = anon
                /shiro/toFailPage = anon
                /shiro/login* = anon
                /shiro/** = authc
            </value>
        </property>
    </bean>

</beans>

创建登陆接口的创建
在登陆接口中,主要的步骤如下

  • 获取subject
  • 封装UsernameAndPasswordToken对象
  • 使用subject进行登陆操作
@RequestMapping(value = "/login", method = RequestMethod.POST)
    public ModelAndView getList(String username, String password) {
        ModelAndView modelAndView = new ModelAndView();
        if (Strings.isNullOrEmpty(username) || Strings.isNullOrEmpty(password)) {
            modelAndView.setViewName("redirect:/shiro/toFailPage");
            return modelAndView;
        }
        //获取当前的subject
        Subject subject = SecurityUtils.getSubject();
        //封装UsernameAndPassword
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            //尝试登陆,如果登录失败会返回不同的异常,这些异常的父类是AuthenticationException
            subject.login(token);
            modelAndView.setViewName("redirect:/shiro/toSuccessPage");
            return modelAndView;
        } catch (AuthenticationException ae) {
            System.out.println("认证出现异常,异常信息:" + ae.getMessage());
            modelAndView.setViewName("redirect:/shiro/toFailPage");
            session.setAttribute("exception",ae);
            return modelAndView;
        }
    }

退出登录接口的创建
退出登录接口的操作非常简单,只要获取当前subject,然后使用其logout方法即可,当前或者在spring-shiro.xml文件中使用/** = logout 的方式也可以实现。

@RequestMapping(value = "/logout",method = RequestMethod.GET)
public ModelAndView loginOut(){
    SecurityUtils.getSubject().logout();
    ModelAndView modelAndView =new ModelAndView("/shiro/login");
    return modelAndView;
}

配置Reamle域
Reamle是在spring-shiro中装配的并且在其中我们还是制定了密码的加密方式,下面来实现这个类,实现这个类需要继承Reanle接口,但是我们可以继承这个接口的实现类,从而减少工作量,继承AuthenticatingRealm类,实现其中的doGetAuthenticationInfo(AuthenticationToken authenticationToken) 的方法即可,这里一般使用DAO层方法,做一些密码的验证和获取。

其步骤如下:

1、将authenticationToken强转为UsernameAndPasswordtoken
2、获取用户名和密码
3、调用数据库查询
4、没有用户,返回 UnknownAccountException
public class CustomReamle extends AuthenticatingRealm {

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)  throws AuthenticationException {
    //强转为UsernameAndPassword,这个对象就是我们在Controller层传递的过来的
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    //获取账户和密码
    String username = token.getUsername();
    String password=new String(token.getPassword());
    //一些处理逻辑,这些应该是数据库的查询
    //主要是检查账户信息和状态,获得账户实体对象,这里实现了逻辑
    //根据数据库查到的结果抛出不同的异常信息
    //UnknownAccountException 未知的账户异常
    //LockedAccountException 账户已被锁定,无法登陆异常
    //当然还有其他的,比如密码错误异常 IncorrectCredentialsException
    if (username.contains("123")){
        throw new UnknownAccountException("账户不存在");
    }

    if ("admin".equals(username)){
        throw new LockedAccountException("账户被锁定,无法登陆!");
    }

    //构建返回对象
    //用户实体对象,这个对象应该是User表的对应类的实体
    Object princial = username;
    //由于在Reamle中,我们的配置了加密方式,因此这里也要进行加密,这个密码是从数据苦中获取的加密后的密码
    //MD5盐值加密,加密2014次,盐值我们不使用,这里设置为null
    SimpleHash md5= new SimpleHash("MD5","123456",null,1024);
    Object pass = md5;
    //获取当前的域名称,通过这些名称可以获取一些参数
    String realName = getName();
    //传递用户对象和用户的加密后的密码,shiro自行比较
    SimpleAuthenticationInfo sm = new SimpleAuthenticationInfo(princial,pass,realName);
    return  sm;
}

先前就是这样的一个流程,在spring-shiro.xml文件中配置一些需要登录才能访问的页面权限,即可简单的实现一些登陆操作!