(转)shiro权限框架详解06-shiro与web项目整合(下)

http://blog.csdn.net/facekbook/article/details/54962975

shiro和web项目整合,实现类似真实项目的应用

  • web项目中认证
  • web项目中授权
  • shiro缓存
  • sessionManager使用
  • 验证码功能实现
  • 记住我功能实现

web项目中认证

实现方式

修改CustomRealm 的 doGetAuthenticationInfo 方法,从数据库中获取用户信息,CustomRealm 返回查询到的用户信息,包括(加密后的密码字符串和salt以及上文中的菜单)。

修改 doGetAuthenticationInfo 方法

@Autowired
private SysService sysService;

/**
 * 用于认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
    //第一步:通过token获取身份信息
    String userCode = (String) token.getPrincipal();

    //从数据库中查询账号信息是否存在
    SysUser sysUser = null;
    try {
        sysUser = sysService.findSysUserByUserCode(userCode);
    } catch (Exception e1) {
        e1.printStackTrace();
    }

    //如果查询不到返回null
    if(sysUser==null){
        return null;
    }

    //第二步:通过获取的身份信息进行数据库查询
    String password = sysUser.getPassword();

    //组装ActiveUser类
    ActiveUser activeUser = new ActiveUser();
    activeUser.setUserid(sysUser.getId());
    activeUser.setUsercode(sysUser.getUsercode());
    activeUser.setUsername(sysUser.getUsername());

    //查询菜单信息
    List<SysPermission> menus = null;
    try {
        menus = sysService.findMenuListByUserId(sysUser.getUsercode());
    } catch (Exception e) {
        e.printStackTrace();
    }
    activeUser.setMenus(menus);

    //得到盐
    String salt = sysUser.getSalt();

    //如果查询到结果返回AuthenticationInfo
    AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(activeUser, password,ByteSource.Util.bytes(salt), "");

    return authenticationInfo;
}
  • 1

设置凭证匹配器

在我们的数据库存储的是MD5散列值,在自定义的realm中需要自定义设置散列算法以及散列次数。这里和前面介绍的散列认证的配置方式类似。

<!-- 自定义的realm -->
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
<!--将匹配器设置到realm中 -->
    <property name="credentialsMatcher" ref="credentialsMatcher" />
</bean>
<!--定义凭证匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 设置hash散列算法 -->
    <property name="hashAlgorithmName" value="md5" />
    <!-- 设置hash散列次数 -->
    <property name="hashIterations" value="1" />
</bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

验证认证功能

数据库存在两条用户数据,具体如下: 
 
其中:张三 的密码是 111111。当然也可以自己修改密码:

SELECT MD5(‘密码‘+‘盐‘)
  • 1
  • 1

如果可能正常登录则没有问题。

授权

实现方式

修改 CustomRealm 中的 doGetAuthorizationInfo 方法从数据库中查询授权信息。 
这里讲解注解式授权和jsp标签授权方法。

修改 doGetAuthorizationInfo 方法

/**
  * 用于授权
  */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

    //获取身份信息,这个字段是在认证通过后返回的,也就是通过执行认证方法返回的AuthenticationInfo类的第一个属性
    ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();

    //通过userId查询数据库获取该身份信息的所有权限。
    List<SysPermission> permissionList = null;
    try {
        permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
       } catch (Exception e) {
        e.printStackTrace();
       }
    List<String> permissions = new ArrayList<>();
    if(permissionList!=null){
        for(SysPermission p:permissionList){
            permissions.add(p.getPercode());
        }
    }

    //查询到权限信息,然后返回权限信息
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    //将查询到的授权信息填充到SimpleAuthorizationInfo中
    simpleAuthorizationInfo.addStringPermissions(permissions);
    return simpleAuthorizationInfo;
}
  • 1

controller类的AOP支持

<!--开启aop,对类代理 -->
<aop:config proxy-target-class="true"></aop:config>
<!-- 开启shiro注解支持 -->
<bean
     class="
org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager" />
</bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在ItemsController类方法上添加注解

//商品信息方法
@RequestMapping("/queryItems")
@RequiresPermissions("item:query")//通过注解的方式进行授权
public ModelAndView queryItems(HttpServletRequest request) throws Exception {

    System.out.println(request.getParameter("id"));

    //调用service查询商品列表
    List<ItemsCustom> itemsList = itemsService.findItemsList(null);

    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("itemsList", itemsList);
    // 指定逻辑视图名
    modelAndView.setViewName("itemsList");

    return modelAndView;
}
  • 1

jsp标签授权

在jsp页面添加shiro taglib

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

shiro包括的jsp标签

标签名称 标签条件(均显示标签内容)
<shiro:authenticated> 登录之后
<shiro:notAuthenticated> 不在登录状态时
<shiro:guest> 用户在没有RememberMe时
<shiro:user> 用户在RememberMe时
<shiro:hasAnyRoles name="abc,123" > 在有abc或者123角色时
<shiro:hasRole name="abc"> 拥有角色abc
<shiro:lacksRole name="abc"> 没有角色abc
<shiro:hasPermission name="abc"> 拥有权限资源abc
<shiro:lacksPermission name="abc"> 没有abc权限资源
<shiro:principal> 显示用户身份名称
<shiro:principal property="username"/> 显示用户身份中的属性值

修改itemsList.jsp文件

    <!-- 具有item:update权限才显示修改链接,没有的话不显示。相当于if(hasPermission(item:update)) -->
<shiro:hasPermission name="item:update">
    <a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a>
</shiro:hasPermission>

授权测试

当调用controller的一个方法时,由于该方法加了@RequiresPermissions("item:query") 注解,shiro会调用realm获取数据库中的权限信息,看 item:query 是否在权限数据中存在,如果不存在就拒绝访问,如果存在就授权通过

当展现一个jsp页面时,页面中如果遇到 <shiro:hasPermission name="item:update"> 标签,shiro调用realm获取数据库中的权限信息,看item:update 是否在权限数据中存在,如果不存在就不显示标签包含内容,如果存在则显示。

在这里只要遇到注解或shiro jsp标签授权,就会调用realm查询数据库,在这里需要引入缓存解决。

shiro缓存

针对授权时频繁查询数据库的问题,引入shiro缓存。

缓存流程

用户认证通过。 
用户第一次授权:调用realm查询数据库。 
用户第二次授权:不调用realm查询数据库,直接从缓存中读取授权信息。

使用 ehcache

添加Ehcache的jar包 
 
配置Ehcache配置文件: 
新建shiro-ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!--diskStore:缓存数据持久化的目录 地址  -->
    <diskStore path="E:\develop\ehcache" />
    <defaultCache
        maxElementsInMemory="1000"
        maxElementsOnDisk="10000000"
        eternal="false"
        overflowToDisk="false"
        diskPersistent="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>
  • 1

配置cacheManager

<!--securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="customRealm" />
    <property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 定义shiro缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>
  • 1

清空缓存

当用户权限修改后,用户再次登录shiro会自动调用realm从数据库获取权限数据,如果在修改权限后想立即清除缓存则可以调用realm的clearCache方法清除。 
CustomRealm 中定义clearCached方法:

    /**
     * 清除缓存方法
     */
    public void clearCache(){
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);   

    }
  • 1

验证码功能实现

实现方式

shiro使用FormAuthenticationFilter进行表单认证,验证码校验的功能应该加在FormAuthenticationFilter中,在认证之前进行验证码校验。

自定义FormAuthenticationFilter

public class CustomFormAuthenticationFilter extends FormAuthenticationFilter{

    /**
     * 原AuthenticationFilter验证方法
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

        //获取正确的验证码和用户输入的验证码进行比对
        HttpServletRequest httpServletRequest = (HttpServletRequest)request;

        HttpSession session = httpServletRequest.getSession();

        //从session获取正确验证码
        String validateCode = (String) session.getAttribute("validateCode");
        //取出页面的验证码
        String randomcode = (String) httpServletRequest.getParameter("randomcode");
        if(validateCode!=null && randomcode!=null && !validateCode.equals(randomcode)){
            //验证码不相同,给shiroLoginFailure属性设置值
            request.setAttribute("shiroLoginFailure","randomcodeError");
            //拒绝访问,不再校验账号和密码
            return true;
        }

        return super.onAccessDenied(request, response);
    }

}
  • 1

配置自定义的FormAuthenticationFilter

在applicationContext-shiro.xml文件中配置

    <!-- 自定义form认证过滤器 -->
    <bean id="formAuthenticationFilter" class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter">
        <!--表单中账号的name属性的值-->
        <property name="usernameParam" value="account"/>
        <!--表单中账号的password属性的值-->
        <property name="passwordParam" value="accountPassword"/>
    </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

修改 shiroFilter 配置

    <!-- web.xml中shiro的filter对应的bean -->
    <!-- shiro的web过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- logiUrl认证提交地址,如果没有认证通过将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
        <property name="loginUrl" value="/login.action" />
        <!-- 认证成功后统一跳转到first.action,建议不配置,shiro认证成功自动到上一个链接 -->
        <property name="successUrl" value="/first.action" />
        <!-- 通过unauthorizedUrl指定没有权限时跳转页面 -->
        <property name="unauthorizedUrl" value="/refuse.jsp" />
        <!-- 自定义filter配置 -->
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
  • 1

修改 LoginController 的 login 方法

    @RequestMapping("/login")
    public String login(HttpServletRequest request)throws Exception{

        //如果登录失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
        String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
        if(exceptionClassName!=null){
            if(UnknownAccountException.class.getName().equals(exceptionClassName)){
                throw new CustomException("账号不存在");
            }else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)){
                throw new CustomException("用户名或密码错误");
            }else if("randomcodeError".equals(exceptionClassName)){
                throw new CustomException("验证码错误");
            }else{
                throw new Exception();//最终在异常处理器生成未知错误
            }
        }
        //此方法不处理登录成功(认证成功),shiro认证成功会自动跳转到上一个请求路径。
        //登录失败还到login页面
        return "login";
    }
  • 1

在登录页面添加验证码

    <TR>
        <TD>用户名:</TD>
        <TD colSpan="2"><input type="text" id="usercode" name="account" style="WIDTH: 130px" />   </TD>
    </TR>
    <TR>
        <TD>密 码:</TD>
        <TD><input type="password" id="pwd" name="accountPassword" style="WIDTH: 130px" /></TD>
    </TR>
    <TR>
        <TD>验证码:</TD>
        <TD><input id="randomcode" name="randomcode" size="8" /> <img id="randomcode_img" src="${baseurl}validatecode.jsp" alt="" width="56" height="20" align=‘absMiddle‘ /> <a href=javascript:randomcode_refresh()>刷新</a></TD>
    </TR> 
  • 1

实现记住我功能

用户登录选择”记住我”选项,本次登录成功会向cookie写身份信息,下次登录从cookie中取出身份信息实现自动登录。

用户身份信息相关类实现 java.io.Serializable 接口

向cookie记录身份信息的对象需要实现序列号接口,如下:

public class ActiveUser implements java.io.Serializable {
public class SysPermission implements java.io.Serializable{

配置 rememberMeManager

<!--配置记住我cookie-->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
    <!-- rememerMe是cookie名称 -->
    <constructor-arg value="rememberMe"/>
    <property name="maxAge" value="2592000"/>
</bean>
<!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="rememberMeCookie"/>
</bean>

添加到securityManager中

    <!--securityManager安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="customRealm" />
        <property name="cacheManager" ref="cacheManager"/>
        <!-- 记住我 -->
        <property name="rememberMeManager" ref="rememberMeManager"/>
    </bean>
  • 1

修改登录页面

<TR>
    <TD></TD>
    <TD><input type="checkbox" name="rememberMe">记住我</TD>
</TR> 

修改rememberMe的input名称

在前面的配置中修改了账号和密码的input的name属性,”记住我”的name属性值也可以修改

    <!-- 自定义form认证过滤器 -->
    <bean id="formAuthenticationFilter" class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter">
        <!--表单中账号的name属性的值-->
        <property name="usernameParam" value="account"/>
        <!--表单中账号的password属性的值-->
        <property name="passwordParam" value="accountPassword"/>
        <!-- 修改记住我的name属性的值 -->
        <property name="rememberMeParam" value="rememberMe"/>
    </bean>
  • 1

测试记住我功能

选择自动登录后,需要查看cookie是否有rememberMe 

使用UserFilter

在前一篇博客中有说明UserFilter的功能如下:

user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查

我们修改applicationContext-shiro.xml配置文件

    <!-- shiro的web过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- logiUrl认证提交地址,如果没有认证通过将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
        <property name="loginUrl" value="/login.action" />
        <!-- 认证成功后统一跳转到first.action,建议不配置,shiro认证成功自动到上一个链接 -->
        <property name="successUrl" value="/first.action" />
        <!-- 通过unauthorizedUrl指定没有权限时跳转页面 -->
        <property name="unauthorizedUrl" value="/refuse.jsp" />
        <!-- 自定义filter配置 -->
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>

        <!-- 过滤器链定义,从上向下顺序执行,一般将/**放在最后面 -->
        <property name="filterChainDefinitions">
            <value>
                <!--静态资源可以匿名访问 -->
                /images/** = anon
                /js/** = anon
                /styles/** = anon
                /validatecode.jsp = anon
                <!-- 请求logout.action地址,shiro去清除session -->
                /logout.action = logout
                <!-- 配置需要授权的url,查询商品需要有商品查询权限 -->
                <!-- /items/queryItems.action = perms[item:query] /items/editItems.action
                    = perms[item:update] -->

                <!-- 配置记住我或认证通过可以访问地址 -->
                /index.jsp = user
                /first.action = user
                /welcome.jsp = user
                <!-- /**=authc 表示所有的url都需要认证才能访问 -->
                /** = authc
            </value>
        </property>
    </bean>

blog项目的下载地址

点击进入下载页面

时间: 2024-10-12 21:22:25

(转)shiro权限框架详解06-shiro与web项目整合(下)的相关文章

(转) shiro权限框架详解06-shiro与web项目整合(上)

http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springMVC+mybatis,所以我们是基于搭建好的项目进行改造的. 将shiro整合到web应用中 登录 退出 认证信息在页面展现,也就是显示菜单 shiro的过滤器 将shiro整合到web应用中 数据库脚步 sql脚步放到项目中,项目上传到共享的资源中,文章最后给出共享url. 去除项目中不使用shi

(转)shiro权限框架详解05-shiro授权

http://blog.csdn.net/facekbook/article/details/54910606 本文介绍 授权流程 授权方式 授权测试 自定义授权realm 授权流程 开始构造SecurityManager环境subject.isPermitted()授权securityManager.isPermitted()执行授权Authorizer执行授权Realm根据身份获取资源权限信息结束 授权方式 Shiro支持三种方式的授权: 编程式:通过写if/else授权代码块完成. Sub

(转)shiro权限框架详解02-权限理论介绍

http://blog.csdn.net/facekbook/article/details/54893042 权限管理解决方案 本文主要介绍权限管理的解决方法: 粗颗粒度和细颗粒度 基于url拦截 使用权限管理框架 粗颗粒度和细颗粒度 什么是粗颗粒度和细颗粒度 在上一文中提到粗颗粒度和细颗粒度,但是没有细讲. 对资源类型的管理称为粗颗粒度权限管理,既只控制到菜单.按钮.方法,粗颗粒度的例子比如:用户具有用户管理的权限,具有导出订单的权限.对资源实例的控制称为细颗粒度权限管理,既控制到数据级别,

(转) shiro权限框架详解04-shiro认证

http://blog.csdn.net/facekbook/article/details/54906635 shiro认证 本文介绍shiro的认证功能 认证流程 入门程序(用户登录和退出) 自定义Realm 散列算法 认证流程 开始构造SecurityManager环境subject.login();提交认证securityManager.login()执行认证Authenticator执行认证Realm根据身份获取验证信息结束 入门程序(用户登录和退出) 创建Java项目 jdk版本:1

Shiro权限管理详解&lt;转载&gt;

1 权限管理1.1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源. 权限管理包括用户身份认证和授权两部分,简称认证授权.对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问. 1.2 用户身份认证1.2.1 概念 身份认证,就是判断一个用户是否为合法用户的处理过程.最常用的简单身份认证方式是系统通过核对用户输入的用户名

Shiro权限管理框架详解

https://www.cnblogs.com/jpfss/p/8352031.html 1 权限管理1.1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源. 权限管理包括用户身份认证和授权两部分,简称认证授权.对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问. 1.2 用户身份认证1.2.1 概念 身份认证,就是判

shiro权限框架与spring框架轻松整合

2017年06月26日 17:53:30 阅读数:419 shiro是一个权限框架,用于管理网站的权限,大到网站登录过滤,小到一个菜单或按钮是否显示,shiro学习起来非常简单,以下是shiro的执行流程图: 看完不懂的请下载shiro全套视频教程: http://pan.baidu.com/s/1jHOX2MM Subject为当前用户,当它访问系统的时候,就会经过SecurityManager安全管理器,安全管理器类似一个中转站,它实际上会让Realm类来处理用户的认证和授权信息,认证和授权

Shiro权限框架简介

http://blog.csdn.net/xiaoxian8023/article/details/17892041 Shiro权限框架简介 2014-01-05 23:51 3111人阅读 评论(37) 收藏 举报  分类: [java框架](25)  版权声明:本文为博主原创文章,未经博主允许不得转载.如需转载请声明:[转自 http://blog.csdn.net/xiaoxian8023 ] 目录(?)[+] 最近加入了gxpt项目组,被安排做权限模块,所以也有幸第一次接触到了Shiro

Shiro权限框架与SpringMVC整合

1.Shiro整合SpringMVC 我们学习Shiro框架肯定是要应用到Web项目上的,所以我们需要整合Shiro和SpringMVC 整合步骤: 第一步:SpringMVC框架的配置 spring-mvc.xml: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xs