认识Shiro框架

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

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

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

Shiro三大组件:

SubjectSubject一般来说代表当前登录的用户,我们可以在自己的代码中很容易的获取到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对基于SpringWeb应用提供了完美的支持 –>  

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

    }







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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • JDK1.8新特性(二):Collectors收集器类

    JDK1.8新特性(二):Collectors收集器类一.什么是Collectors?Java8API添加了一个新的抽象称为流Stream,我们借助StreamAPI可以很方便的操作流对象。Stream中有两个方法collect和collec

  • 『数据可视化』基于Python的数据可视化工具「建议收藏」

    『数据可视化』基于Python的数据可视化工具「建议收藏」如何做Python的数据可视化?pyecharts是一个用于生成Echarts图表的类库。Echarts是百度开源的一个数据可视化JS库。主要用于数据可视化。一、安装pyecharts兼容Python2和Python3。目前版本为0.1.4pipinstallpyecharts二、入门首先开始来绘制你的第一个图表

  • linux route add 接口,route add命令如何使用「建议收藏」

    linux route add 接口,route add命令如何使用「建议收藏」routeadd命令用于在本地IP路由表中显示和修改条目,使用不带参数的ROUTE可以显示帮助,代码为【route[-f][-p][command[destination][masknetmask]….】。routeadd命令使用情况:一、具体功能该命令用于在本地IP路由表中显示和修改条目。使用不带参数的ROUTE可以显示帮助。二、语法详解route[-f][-p][c…

  • UPX 脱壳初见

    UPX 脱壳初见1.壳是什么?加壳一般是指保护程序资源的方法.脱壳一般是指除掉程序的保护,用来修改程序资源.病毒加壳技术与脱壳杀毒方法:壳是什么?脱壳又是什么?这是很多经常感到迷惑和经常提出的问题,其实这个问题一点也不幼稚。壳,在我们的印象中,它的作用就是保护,例如龟壳,这是传统意义上的壳,通常被用来保护自己;今天我们讨论的壳是程序的壳,它的功能和一般意义上的壳有相同的地方,它们都是保护作用,在一…

  • linux tty_linux tty

    linux tty_linux ttyLinux中的tty与pts终端是一种字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备。tty是Teletype的缩写。 Teletype是最早出现的一种终端设备——可以称作电传打字机,由Teletype公司生产。tty在Linux系统的设备特殊文件目录/dev/下。终端特殊设备文件一般有以下几种:1、串行端口终端(/dev/ttySn)串行端口终端(Serial Port Terminal)是使用计算机串行端口连接的终端设备。计算机把每个串行端口都看作是一个字符设备。有段时间这

  • 用户头像上传_头像使用

    用户头像上传_头像使用上传头像上传头像-持久层SQL语句的规划将对应文件保存在操作系统上,然后在把这个文件路径给记录,因为记录路径是非常便捷和方便,将来如果要打开这个文件可以依据这个路径去找到这个文件。在数据库中需要保存这个文件的路径即可。将所有的静态资源(图片、文件、其他资源文件)方法某台电脑上,在把这台电脑作为一台单独的服务器使用。对应是一个更新用户avatar字段的sql语句。updatet_usersetavatar=?,modified_user=?,modified=?whereuid=?设

    2022年10月25日

发表回复

您的电子邮箱地址不会被公开。

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