shiro:安全(权限)框架,

【用途】:1)验证用户

      2)对用户执行访问控制,如: 判断用户是否拥有角色admin;判断用户是否拥有访问的权限

      3)在任何环境下使用 Session API,可以使用多个用户数据源。例如一个是oracle用户库,另外一个是mysql用户库

       4)单点登录(SSO)功能,“Remember Me”服务 ,类似购物车的功能,shiro官方建议开启


s【shiro的4大API】

Authentication —— 认证,用户身份识别,常被称为用户“登录”,who are you?

Authorization —— 授权,访问控制过程,决定“谁”访问“什么”,who can do what?

Session Management —— 会话管理,用户session管理器,用户相关的时间敏感的状态

Cryptography —— 密码加密,把JDK中复杂的密码加密方式进行封装,保护或隐藏数据防止被偷窥


shiro包含3个核心组件:Subject、SecurityManager和Realms

Subject 是与程序进行交互的对象,可以是人也可以是服务或者其他,通常就理解为用户。

        所有Subject 实例都必须绑定到一个SecurityManager上。我们与一个 Subject 交互,

运行时shiro会自动转化为与 SecurityManager交互的特定 subject的交互。

SecurityManager 是 Shiro的核心,初始化时协调各个模块运行。当SecurityManager协调完毕,

                SecurityManager 会被单独留下,且我们只需要去操作Subject即可,无需操作SecurityManager 。

Realms在 Shiro中作为应用程序和安全数据之间的“桥梁”或“连接器”。它获取安全数据来判断subject是否能够登录,

      subject拥有什么权限。有点类似DAO。在配置realms时,需要至少一个realm。而且Shiro提供了一些常用的 Realms来连接数据源,

      如LDAP数据源的JndiLdapRealm,JDBC数据源的JdbcRealm,ini文件数据源的IniRealm,properties文件数据源的PropertiesRealm等等。

      我们也可以插入自己的 Realm实现来代表自定义的数据源。像其他组件一样,Realms也是由SecurityManager控制。


 1. 引入shiro需要的包。使用maven的项目中,在pom.xml增加以下依赖:

                <!-- shiro权限管理 -->

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-core</artifactId>

<version>1.1.0</version>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-web</artifactId>

<version>1.1.0</version>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-spring</artifactId>

<version>1.1.0</version>

</dependency>

<!-- shiro权限管理 end -->

2. 在项目中增加shiro配置。在spring配置文件目录下新建spring-shiro.xml。内容如下:

       <!-- Shiro权限管理配置 -->

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- shiro通过一个filter控制权限-->

<property name="securityManager" ref="securityManager" />

  <property name="loginUrl" value="/login/execute.do" />  <!-- 登陆页 -->

<property name="successUrl" value="/index.jsp" />  <!-- 登陆成功之后跳转的页面 -->

  <property name="unauthorizedUrl" value="/login/execute.do" /> <!-- 用户在请求无权限的资源时,跳转到这个url -->

  <property name="filters">

  <util:map>

<entry key="authc">

 <bean class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter" />

  </entry>

 </util:map>

</property>

<property name="filterChainDefinitions"> <!-- 配置访问url资源需要用户拥有什么权限 配置的优先级由上至下-->

<value>

/=anon

/template/main.jsp=user

<!-- api用户信息 -->

  /api/createApiUser**=perms[api:user:create]  <!-- 权限可以用":"分级,如拥有api权限相当于拥有api*权限(父权限涵盖子权限) -->

/api/updateApiUser**=perms[api:user:update]

  /api/*User*=perms[api:user:view]

  /template/apiUserManage/**=perms[api:user:view] 

  <!-- api接口管理 -->

  /api/*Interface*=perms[api:user:interface]

  <!-- api统计数据 -->

  /api/querySummaryData**=perms[api:data]

  /template/apiSumData/**=perms[api:data]

              /api/**=perms[api:*]

                                ...

/**=anon

</value>

</property>

</bean>

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<property name="realm" ref="permissionsRealm" />  <!-- 自定义登陆及获取权限的源 -->

</bean>


  <!-- shiro权限管理配置 end -->

3、在web.xml中增加shiro filter的配置:

   <context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

classpath*:applicationContext.xml,

classpath*:spring-*.xml // 

</param-value>

    </context-param>

    ...

    <filter>

        <filter-name>shiroFilter</filter-name>

        <filter-class>

            org.springframework.web.filter.DelegatingFilterProxy

        </filter-class>

    </filter>

     

    <filter-mapping>

        <filter-name>shiroFilter</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>


4、定义登陆及获取权限的源。


/**

 * 认证实现类

 * 

 * @author kexm

 * 

 */

@Service("permissionsRealm")

public class PermissionsRealm extends AuthorizingRealm {

    @Autowired

    private AccountDao accountDao;


    @Autowired

    private GroupDao groupDao;


    private Account acc;


    private static LogUtil log = LogUtil.getLogger(PermissionsRealm.class);


    /**

     * 用户权限源(shiro调用此方法获取用户权限,至于从何处获取权限项,由我们定义。)

     */

    @Override

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        log.info("method[doGetAuthorizationInfo] begin.");

        if (acc != null) {

            if(acc.getAdminType() == 2){//超级管理员 始终拥有所有权限

                info.addStringPermission("*");

                return info;

            }

            try {

                List<UserGroup> gList = accountDao.getUserGroups(acc.getLoginName());

                for (UserGroup g: gList) { //获取用户的组

                    log.info("method[doGetAuthorizationInfo] group<" + g.getName() + ">");

                    List<Permission> pList = groupDao.getGroupPerms(g.getId());

                    for (Permission p: pList) { //获取组内权限

                        log.info("method[doGetAuthorizationInfo] perm<" + p.getName() + "," + p.getPermList() + ">");

                        String permList = p.getPermList();

                        if (permList != null && !"".equals(permList)) {

                            String[] perms = p.getPermList().split(",");

                            for (String perm: perms) {//分别放入容器   (权限以字符串形式呈现,如"api:data"等,和spring-shiro.xml中的配置相对应)

                                log.info("method[doGetAuthorizationInfo] add perm<" + perm + ">");

                                info.addStringPermission(perm);

                            }

                        }

                    }

                }

                return info;//将用户权限返回给shiro

            } catch (Exception e) {

                log.error("method[doGetAuthorizationInfo] e.message<" + e.getMessage() + "> e<" + e + ">", e);

            }

        }

        return null;

    }


    /**

     * 用户登录验证源(shiro调用此方法执行认证)

     */

    @Override

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authtoken) throws AuthenticationException {

        log.info("method[doGetAuthenticationInfo] begin.");

        UsernamePasswordToken token = (UsernamePasswordToken) authtoken;

        SimpleAuthenticationInfo authenticationInfo = null;

        String userName = token.getUsername();

        String password = new String(token.getPassword());

        Login conf = DefaultConfigure.config.getLogin();

        String MD5pwd = MD5Util.generateSignature(conf.getSalt(), password);

        try {

            if (userName != null && !"".equals(userName)) {

                acc = accountDao.login(userName, MD5pwd);

            }

            if (acc != null) {

                doGetAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());

                authenticationInfo = new SimpleAuthenticationInfo(token.getUsername(), token.getPassword(), getName());

                return authenticationInfo;

            }

        } catch (Exception e) {

            log.error("method[doGetAuthenticationInfo] acc<" + acc + "> message<" + e.getMessage() + "> e<" + e + ">",

                e);

        }

        return null;

    }


}


5、shiro中,使用subject管理用户。可以把subject理解为shiro存储用户信息的容器和操纵用户的工具。

   有了前几步的配置,便可以使用以下代码登入登出,并享受shiro的url权限控制了。


        //登入

        UsernamePasswordToken token = new UsernamePasswordToken(loginName, password);

        Subject user = SecurityUtils.getSubject();

        user.login(token);

        //使用shiro自带的session存储用户信息 独立于httpSession

        Session ss = user.getSession().setAttribute("userInfo", acc);

        //登出

        SecurityUtils.getSubject().logout();


6、在页面中使用shiro标签。假如我们要让有权限的用户看到某些菜单或按钮,可以用以下方式。

    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

    <shiro:hasPermission name="api:data">

        who has permission can see

    </shiro:hasPermission>