认识Shiro框架

认识Shiro框架Shiro三大组件:Subject:Subject一般来说代表当前登录的用户,我们可以在自己的代码中很容易的获取到Subject对象SecurityManager:它是shiro框架的核心。Subject代表某一个用户,而SecurityManager就是对这些Subject进行管理的对象,在web项目中使用shiro的时候,我们通常在xml文件中配置好SecurityManager

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

Shiro三大组件:

Subject:Subject一般来说代表当前登录的用户,我们可以在自己的代码中很容易的获取到Subject对象

SecurityManager:它是shiro框架的核心。Subject代表某一个用户,而SecurityManager就是对这些Subject进行管理的对象,在web项目中使用shiro的时候,我们通常在xml文件中配置好SecurityManager对象。Shiro用它来管理内部组件实例,并通过它来提供安全管理的各种服务。

Realms:当对用户执行认证和授权访问验证时,shiro为从应用配置的Realm中查找用户及权限信息。从这个意义上讲,Realm实质上是一个安全相关的DAO,它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须指定一个Realm,用于认证和授权,配置多个Realm是可以的,但至少需要一个,shiro中使用Realms这个概念表示与数据进行交互的那一层,封装了数据源连接的细节,我们可以实现不同的realms来连接不同的数据源,通过realms读取用户数据用于认证和鉴权。


Shiro认证过程:

1、收集实体信息

UsernamePasswordToken token = new UsernamePasswordToken(username, password);UsernamePasswordToken支持最常见的用户名/密码的认证机制,由于它实现了RememberMeAuthenticationToken接口,我们可以通过令牌设置“记住我”的功能。

2、提交实体/凭据信息

Subject currentUser = SecurityUtils.getSubject();currentUser.login(token); 收集了实体/凭据信息之后,我们可以通过SecurityUtils工具类,获取当前的用户,然后通过调用login方法提交认证。

3、认证处理

try {  

            System.out.println(“对用户[” + username + “]进行登录验证..验证开始“);  

            currentUser.login(token);  

            System.out.println(“对用户[” + username + “]进行登录验证..验证通过“);  

            resultPageURL = “main”;  

        }catch(UnknownAccountException uae){  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,未知账户“);  

            request.setAttribute(“message_login”, “未知账户“);  

        }catch(IncorrectCredentialsException ice){  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,错误的凭证“);  

            request.setAttribute(“message_login”, “密码不正确“);  

        }catch(LockedAccountException lae){  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,账户已锁定“);  

            request.setAttribute(“message_login”, “账户已锁定“);  

        }catch(ExcessiveAttemptsException eae){  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,错误次数过多“);  

            request.setAttribute(“message_login”, “用户名或密码错误次数过多“);  

        }catch(AuthenticationException ae){  

            //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,堆栈轨迹如下“);  

            ae.printStackTrace();  

            request.setAttribute(“message_login”, “用户名或密码不正确“);  

        }


登出操作:

登出操作可以通过调用subject.logout()来删除你的登录信息。

SecurityUtils.getSubject().logout();


授权操作:

授权有着三要素:权限,角色和用户。 

权限:

权限是Apache Shiro安全机制最核心的元素。它在应用程序中明确声明了被允许的行为和表现。一个格式良好好的权限声明可以清晰表达出用户对该资源拥有的权限。

角色:

Shiro支持两种角色模式: 

1、传统角色:一个角色代表着一系列的操作,当需要对某一操作进行授权验证时,只需判断是否是该角色即可。这种角色权限相对简单、模糊,不利于扩展。 

2、权限角色:一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有该权限。这种角色权限可以对该角色进行详细的权限描述,适合更复杂的权限设计。


内置的过滤链FilterChain:

如  /admin = authc,roles[admin]      表示用户必需已通过认证,并拥有admin角色才可以正常发起‘/admin’请求;

   /edit = authc,perms[admin:edit]  表示用户必需已通过认证,并拥有admin:edit权限才可以正常发起‘/edit’请求。


新建工程配置shiro:

(1)web.xml配置文件里面加: 

<!– 配置Shiro过滤器,先让Shiro过滤系统接收到的请求 –>  

    <!– 这里filter-name必须对应applicationContext.xml中定义的<bean id=”shiroFilter”/> –>  

    <!– 使用[/*]匹配所有请求,保证所有的可控请求都经过Shiro的过滤 –>  

    <!– 通常会将此filter-mapping放置到最前面(即其他filter-mapping前面),以保证它是过滤器链中第一个起作用的 –>  

    <filter>  

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

        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  

        <init-param>  

            <!– 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 –>  

            <param-name>targetFilterLifecycle</param-name>  

            <param-value>true</param-value>  

        </init-param>  

    </filter>  

    <filter-mapping>  

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

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

</filter-mapping>


(2)Application.xml配置文件里面加: 

<!– 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录的类为自定义的ShiroDbRealm.java –>  

    <bean id=”myRealm” class=”fy.com.realm.MyRealm”/>    

    <!– Shiro默认会使用Servlet容器的Session,可通过sessionMode属性来指定使用Shiro原生Session –>  

    <!– 即<property name=”sessionMode” value=”native”/>,详细说明见官方文档 –>  

    <!– 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用‘realms’属性代替 –>  

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

        <property name=”realm” ref=”myRealm”/>  

    </bean>     

    <!– Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 –>  

    <!– Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 –>  

    <bean id=”shiroFilter” class=”org.apache.shiro.spring.web.ShiroFilterFactoryBean”>  

        <!– Shiro的核心安全接口,这个属性是必须的 –>  

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

        <!– 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的“/login.jsp”页面 –>  

        <property name=”loginUrl” value=”/”/>  

        <!– 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在LoginController里硬编码为main.jsp了) –>  

        <!– <property name=”successUrl” value=”/system/main”/> –>  

        <!– 用户访问未对其授权的资源时,所显示的连接 –>  

        <property name=”unauthorizedUrl” value=”/”/>  

        <!– Shiro连接约束配置,即过滤链的定义 –>   

        <!– 下面value值的第一个‘/’代表的路径是相对于HttpServletRequest.getContextPath()的值来的 –>  

        <!– anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 –>  

        <!– authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter –>  

        <property name=”filterChainDefinitions”>  

            <value>  

                /mydemo/login=anon  

                /mydemo/getVerifyCodeImage=anon  

                /main**=authc  

                /user/info**=authc  

                /admin/listUser**=authc,perms[admin:manage]  

            </value>  

        </property>  

    </bean>     

    <!– 保证实现了Shiro内部lifecycle函数的bean执行 –>  

<bean id=”lifecycleBeanPostProcessor” class=”org.apache.shiro.spring.LifecycleBeanPostProcessor”/>

(3)自定义的Realm:

MyRealm.java:

package com.jadyer.realm;      

/** 

 * 自定义的指定Shiro验证用户登录的类 

 * @see 在本例中定义了2个用户:jadyer和玄玉,jadyer具有admin角色和admin:manage权限,玄玉不具有任何角色和权限 

 * @create Sep 29, 2013 3:15:31 PM 

 * @author 玄玉<http://blog.csdn.net/jadyer> 

 */  

public class MyRealm extends AuthorizingRealm {  

    /** 

     * 为当前登录的Subject授予角色和权限 

     * @see 经测试:本例中该方法的调用时机为需授权资源被访问时 

     * @see 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache 

     * @see 个人感觉若使用了Spring3.1开始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache 

     * @see 比如说这里从数据库获取权限信息时,先去访问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache 

     */  

    @Override  

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){  

        //获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next()  

        String currentUsername = (String)super.getAvailablePrincipal(principals);  

        SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();   

        if(null!=currentUsername && “jadyer”.equals(currentUsername)){  

            //添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色    

            simpleAuthorInfo.addRole(“admin”);  

            //添加权限  

            simpleAuthorInfo.addStringPermission(“admin:manage”);  

            System.out.println(“已为用户[jadyer]赋予了[admin]角色和[admin:manage]权限“);  

            return simpleAuthorInfo;  

        }else if(null!=currentUsername && “玄玉“.equals(currentUsername)){  

            System.out.println(“当前用户[玄玉]无授权“);  

            return simpleAuthorInfo;  

        }  

        //若该方法什么都不做直接返回null的话,就会导致任何用户访问/admin/listUser.jsp时都会自动跳转到unauthorizedUrl指定的地址  

        //详见applicationContext.xml中的<bean id=”shiroFilter”>的配置  

        return null;  

    }           

    /** 

     * 验证当前登录的Subject 

     * @see 经测试:本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()时 

     */  

    @Override  

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {  

        //获取基于用户名和密码的令牌  

        //实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的  

        //两个token的引用都是一样的,本例中是org.apache.shiro.authc.UsernamePasswordToken@33799a1e  

        UsernamePasswordToken token = (UsernamePasswordToken)authcToken;  

        System.out.println(“验证当前Subject时获取到token为” + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));  

        //此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息  

        //说白了就是第一个参数填登录用户名,第二个参数填合法的登录密码(可以是从数据库中取到的,本例中为了演示就硬编码了)  

        //这样一来,在随后的登录页面上就只有这里指定的用户和密码才能通过验证  

        if(“jadyer”.equals(token.getUsername())){  

            AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(“jadyer”, “jadyer”, this.getName());  

            this.setSession(“currentUser”, “jadyer”);  

            return authcInfo;  

        }else if(“玄玉“.equals(token.getUsername())){  

            AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(“玄玉“, “xuanyu”, this.getName());  

            this.setSession(“currentUser”, “玄玉“);  

            return authcInfo;  

        }  

        //没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常  

        return null;  

    }               

    /** 

     * 将一些数据放到ShiroSession中,以便于其它地方使用 

     * @see 比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到 

     */  

    private void setSession(Object key, Object value){  

        Subject currentUser = SecurityUtils.getSubject();  

        if(null != currentUser){  

            Session session = currentUser.getSession();  

            System.out.println(“Session默认超时时间为[” + session.getTimeout() + “]毫秒“);  

            if(null != session){  

                session.setAttribute(key, value);  

            }  

        }  

    }  

}

(4)LoginController.java部分代码:

public String login(HttpServletRequest request){  

        String resultPageURL = InternalResourceViewResolver.FORWARD_URL_PREFIX + “/”;  

        String username = request.getParameter(“username”);  

        String password = request.getParameter(“password”);  

        //获取HttpSession中的验证码  

        String verifyCode = (String)request.getSession().getAttribute(“verifyCode”);  

        //获取用户请求表单中输入的验证码  

        String submitCode = WebUtils.getCleanParam(request, “verifyCode”);  

        System.out.println(“用户[” + username + “]登录时输入的验证码为[” + submitCode + “],HttpSession中的验证码为[” + verifyCode + “]”);  

        if (StringUtils.isEmpty(submitCode) || !StringUtils.equals(verifyCode, submitCode.toLowerCase())){  

            request.setAttribute(“message_login”, “验证码不正确“);  

            return resultPageURL;  

        }  

        UsernamePasswordToken token = new UsernamePasswordToken(username, password);  

        token.setRememberMe(true);  

        System.out.println(“为了验证登录用户而封装的token为” + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));  

        //获取当前的Subject  

        Subject currentUser = SecurityUtils.getSubject();  

        try {  

            //在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查  

            //每个Realm都能在必要时对提交的AuthenticationTokens作出反应  

            //所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法  

            System.out.println(“对用户[” + username + “]进行登录验证..验证开始“);  

            currentUser.login(token);  

            System.out.println(“对用户[” + username + “]进行登录验证..验证通过“);  

            resultPageURL = “main”;  

        }catch(UnknownAccountException uae){  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,未知账户“);  

            request.setAttribute(“message_login”, “未知账户“);  

        }catch(IncorrectCredentialsException ice){  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,错误的凭证“);  

            request.setAttribute(“message_login”, “密码不正确“);  

        }catch(LockedAccountException lae){  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,账户已锁定“);  

            request.setAttribute(“message_login”, “账户已锁定“);  

        }catch(ExcessiveAttemptsException eae){  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,错误次数过多“);  

            request.setAttribute(“message_login”, “用户名或密码错误次数过多“);  

        }catch(AuthenticationException ae){  

            //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景  

            System.out.println(“对用户[” + username + “]进行登录验证..验证未通过,堆栈轨迹如下“);  

            ae.printStackTrace();  

            request.setAttribute(“message_login”, “用户名或密码不正确“);  

        }  

        //验证是否登录成功  

        if(currentUser.isAuthenticated()){  

            System.out.println(“用户[” + username + “]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化操作)”);  

        }else{  

            token.clear();  

        }  

        return resultPageURL;  

    }







版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/196045.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • vue的form表单提交_axios提交表单

    vue的form表单提交_axios提交表单利用v-model能比较便捷地上传用户信息的数据,不用一个个参数地拼接。直接在data根据要传的字段定义一个对象,再利用双向绑定得到值。下面写了传json格式跟formData格式的两种情况,根据实际参考<template><divclass=”from_box”><formaction=””><inputty……

    2022年10月7日
  • 搭建smtp邮件服务器_smtp服务器怎么填

    搭建smtp邮件服务器_smtp服务器怎么填4.“传递”选项卡在“默认SMTP虚拟服务器属性”对话框中点击“传递”选项卡,如图所示。该选项卡用来设定邮件一次发送不成功时,SMTP服务器如何处理这些邮件。在大多数情况下,每个虚拟服务器都尽量在消息一抵达消息队列后就进行发送;可是,如果下一个中继段的服务器发生了某个临时性的问题,或者网络中发生了某种通信故障,那么虚拟服务器就会采取适当的措施,例如对消息进行排队,准备再次重试,或者变更消息路由。如…

    2022年10月3日
  • Jenkins(4)docker容器内部修改jenkins容器时间[通俗易懂]

    Jenkins(4)docker容器内部修改jenkins容器时间[通俗易懂]前言用docker搭建的Jenkins环境时间显示和我们本地时间相差8个小时,需修改容器内部的系统时间查看时间查看系统时间date-R进入docker容器内部,查看容器时间dockere

    2022年7月29日
  • 如何做好数据安全治理「建议收藏」

    如何做好数据安全治理「建议收藏」  数据安全问题贯穿数据全生命周期的各个环节。在新形势下,要做好数据安全治理,就要做好企业的数据安全防护能力建设,建立起一个强保障且动态化的安全保护机制。这个机制的攻坚点主要是三个方面:完善数据安全治理规划,提高数据安全技术防护能力,和加强数据安全审计。  1.完善数据安全治理规划  (1)评估数据安全现状  一般来说,组织进行数据安全治理的目标主要是两个:一是实现组织的合规保障;二是实现数据的充分开发利用,在安全的基础上谋发展。组织在进行数据安全治理时,应在这两个大方向的指引下,对组织内部的数

    2022年5月18日
  • 实用ExtJS教程100例-003:进度条对话框Ext.MessageBox.progress

    实用ExtJS教程100例-003:进度条对话框Ext.MessageBox.progress在上一篇内容中我们介绍了三种常用的MessageBox提示框,在这篇文章中,我们将演示如何在对话框中使用进度条。进度条对话框我们可以使用下面的代码来在MessageBox中显示一个进度条:Ext.get(“btn4”).on(“click”,function(){Ext.MessageBox.progress(“进度”,”…

    2022年6月19日
  • 最短路径四大算法「建议收藏」

    最短路径四大算法「建议收藏」熟悉的最短路算法就几种:bellman-ford,dijkstra,spfa,floyd。首先说明一点,就是关于负环的问题。bellman-ford可以用于边权为负的图中,图里有负环也可以,如果有负环,算法会检测出负环。时间复杂度O(VE);dijkstra只能用于边权都为正的图中。时间复杂度O(n2);spfa是个bellman-ford的优化算法,本质是bellman-for

    2022年6月3日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号