权限安全管理框架---shiro入门案例

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。
这篇打算记录一个shiro关于登录的认证和授权的入门案例。

一、认证+授权

第一步:导入shiro-all.jar
第二步:在web.xml中配置一个spring用于整合shiro的过滤器

<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>

第三步:在spring配置文件中配置一个bean,id必须和上面的过滤器名称相同

<!-- 配置一个工厂bean,用于创建shiro框架用到过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <!-- 注入安全管理器 -->
    <property name="securityManager" ref="securityManager"></property>
    <!-- 注入当前系统的登录页面 -->
    <property name="loginUrl" value="/login.jsp"/>
    <!-- 注入成功页面 -->
    <property name="successUrl" value="/index.jsp"/>
    <!-- 注入权限不足提示页面 -->
    <property name="unauthorizedUrl" value="/unauthorizedUrl.jsp"/>
    <!-- 注入URL拦截规则 -->
    <property name="filterChainDefinitions">
        <value>
            /css/** = anon
            /images/** = anon
            /js/** = anon
            /login.jsp* = anon
            /validatecode.jsp* = anon
            /userAction_login.action = anon
            /page_base_staff.action = perms["staff"]
            /* = authc
        </value>
    </property>
</bean>

第四步:在spring配置文件中注册安全管理器,为安全管理器注入realm

<!-- 注册自定义realm -->
<bean id="bosRealm" class="com.zyj.bos.shiro.BOSRealm"></bean>

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <!-- 注入上面的realm -->
    <property name="realm" ref="bosRealm"/>
</bean>

第五步:自定义一个BOSRealm

public class BOSRealm extends AuthorizingRealm {
    @Resource
    private IUserDao userDao;
    /**
     * 认证方法
     */
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {

        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();// 从令牌中获得用户名

        User user = userDao.findUserByUsername(username);
        if (user == null) {
            // 用户名不存在
            return null;
        } else {
            // 用户名存在
            String password = user.getPassword();// 获得数据库中存储的密码
            // 创建简单认证信息对象
            /***
             * 参数一:签名,程序可以在任意位置获取当前放入的对象
             * 参数二:从数据库中查询出的密码
             * 参数三:当前realm的名称
             */
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,
                    password, this.getClass().getSimpleName());
            return info;//返回给安全管理器,由安全管理器负责比对数据库中查询出的密码和页面提交的密码
        }
    }
    /**
     * 授权方法
     */
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //根据当前登录用户查询其对应的权限,授权
        User user = (User) principals.getPrimaryPrincipal();
        List<Function> list = null;
        if(user.getUsername().equals("admin")){
            //当前用户是超级管理员,查询所有权限,为用户授权
            list = functionDao.findAll();
        }else{
            //普通用户,根据用户id查询对应的权限
            list = functionDao.findListByUserid(user.getId());
        }
        for (Function function : list) {
            info.addStringPermission(function.getCode());
        }
        return info;
    }
}

第六步:完善UserAction的login方法

public String login(){
    //生成的验证码
    String key = (String) ServletActionContext.getRequest().getSession().getAttribute("key");
    //判断用户输入的验证码是否正确
    if(StringUtils.isNotBlank(checkcode) && checkcode.equals(key)){
        //验证码正确
        //获得当前用户对象
        Subject subject = SecurityUtils.getSubject();//状态为“未认证”
        String password = model.getPassword();
        password = MD5Utils.md5(password);
        //构造一个用户名密码令牌
        AuthenticationToken token = new UsernamePasswordToken(model.getUsername(), password);
        try{
            subject.login(token);
        }catch (UnknownAccountException e) {
            e.printStackTrace();
            //设置错误信息
            this.addActionError(this.getText("usernamenotfound"));
            return "login";
        }catch (Exception e) {
            e.printStackTrace();
            //设置错误信息
            this.addActionError(this.getText("loginError"));
            return "login";
        }
        //获取认证信息对象中存储的User对象
        User user = (User) subject.getPrincipal();
        ServletActionContext.getRequest().getSession().setAttribute("loginUser", user);
        return "home";
    }else{
        //验证码错误,设置错误提示信息,跳转到登录页面
        this.addActionError(this.getText("validateCodeError"));
        return "login";
    }
}

第七步:在自定义Realm中编写授权方法

/**
 * 授权方法
 */
protected AuthorizationInfo doGetAuthorizationInfo(
        PrincipalCollection principals) {
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //根据当前登录用户查询其对应的权限,授权
    User user = (User) principals.getPrimaryPrincipal();
    List<Function> list = null;
    if(user.getUsername().equals("admin")){
        //当前用户是超级管理员,查询所有权限,为用户授权
        list = functionDao.findAll();
    }else{
        //普通用户,根据用户id查询对应的权限
        list = functionDao.findListByUserid(user.getId());
    }
    for (Function function : list) {
        info.addStringPermission(function.getCode());
    }
    return info;
}

二、shiro提供的权限控制方式

1. URL拦截权限控制
这里写图片描述
2. 方法注解权限控制
第一步:在spring配置文件中开启shiro的注解支持

<!-- 开启shiro注解 -->
<!-- 自动代理 -->
<bean id="defaultAdvisorAutoProxyCreator" 
    class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
    <!-- 强制使用cglib为Action创建代理对象 -->
    <property name="proxyTargetClass" value="true"></property>
</bean>

<!-- 切面类 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/>

第二步:在Action的方法上使用shiro的注解描述执行当前方法需要具有的权限

/**
 * 批量删除功能(逻辑删除)
 * @return
 */
@RequiresPermissions(value="staff")//执行当前方法需要具有staff权限
@RequiresRoles(value="manager")//执行当前方法需要拥有manager角色
public String delete(){
    staffService.deleteBatch(ids);
    return "list";
}

第三步:修改BaseAction的构造方法
这里写图片描述
第四步:在struts.xml中配置全局异常捕获,统一跳转到权限不足的页面
这里写图片描述

三、页面标签权限控制

第一步:在jsp页面中引入shiro的标签

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

第二步:使用shiro的标签根据当前用户拥有的权限动态展示页面元素

<shiro:hasPermission name="staff">
        {
            id : 'button-delete',
            text : '作废',
            iconCls : 'icon-cancel',
            handler : doDelete
        },
</shiro:hasPermission>

四、使用ehcache缓存权限数据

第一步:导入ehcache的jar包到项目中,com.springsource.net.sf.ehcache-1.6.2.jar
第二步:提供ehcache的xml配置文件(可以从jar包中获得)

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
</ehcache>

第三步:在spring配置文件中注册一个缓存管理器,并注入给安全管理器
这里写图片描述

猜你喜欢

转载自blog.csdn.net/code_shadow/article/details/80330940