Shiro快速入门

摘要:
毕竟,Shiro安全框架仍然在项目中普遍使用。ApacheShiro的基本概念在这里不会一一描述。它们可以在互联网上获得。它们主要与录制代码有关,因此您可以快速学习使用它们。1.准备Shiro相关配置文件xml˂!

写在前面:

  最近项目中使用了Shiro,虽然不是自己在负责这一块,但还是抽空学习了下,也可以让自己对shiro有基本的了解。毕竟Shiro安全框架在项目中还是挺常用的。

  对于Apache Shiro的基本概念就不在这里一一描述了,资料网上都有,主要还是记录下代码相关的,能够先让自己快速学会使用。

  这里的demo(可以测试登录认证,登出,以及授权)主要使用的是ssh框架,所以前提是要将ssh的项目框架搭起来,并导入shiro相关的jar包,然后才是编写配置shiro的相关代码。

  1.shiro相关配置文件的编写

  web.xml

<!-- shiro 过滤器 start -->
    <!--spring容器查找名字为shiroFilter(filter-name)的bean并把所有Filter的操作委托给它。-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <!-- 设置true由servlet容器控制filter的生命周期 -->
        <init-param>
            <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>
    <!-- shiro 过滤器 end -->

  shiro的配置文件applicationContext-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

        <!--配置自定义Realm,需要继承AuthorizingRealm-->
        <bean id="myRealm" class="com.myshiro.shiro.MyRealm">
        </bean>

        <!--配置SecurityManager-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="myRealm"/>
        </bean>

        <!--实现自己的登出过滤器-->
        <bean id="signOutFilter" class="com.myshiro.shiro.SignOutFilter">
            <!--配置登出重定向的路径-->
            <property name="redirectUrl" value="/logout.jsp"/>
        </bean>

        <!-- 配置 ShiroFilter bean: 该 bean 的 id 必须和 web.xml 文件中配置的 shiro filter 的 name 一致  -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager"/>
            <!--配置登录页面,当未认证发送请求时,默认跳转的页面,即登录页面-->
            <property name="loginUrl" value="/views/login.jsp"/>
            <!--没有授权默认跳转的页面-->
            <property name="unauthorizedUrl" value="/noPermission.jsp"/>
            <!--配置登出过滤器-->
            <property name="filters">
                <map>
                    <entry key="logout" value-ref="signOutFilter"></entry>
                </map>
            </property>
            <!--配置资源过滤器链-->
            <property name="filterChainDefinitions">
                <value>
                    <!--anon表示不拦截,直接放行-->
                    /login = anon
                    <!-- authc 表示需要认证后才能访问,即需要登录后才可以访问,否则跳转到上面配置的登录页面-->
                    /authTest = authc
                    <!-- perms 表示需要有对应的权限才能访问的,
                        此处perms表示要登录认证成功,并且有对应的pernissionTest权限才可以进行访问
                        浏览器输入../permissionTest,回车,如果没有通过登录认证,则跳转到上面配置的登录页面;否则去进行授权验证,
                        如果,授权验证没有通过,则跳转到上面配置的未授权跳转的页面,否则可以访问对应的资源
                    -->
                    /permissionTest =  perms[/permissionTest]
                    <!--当角色是admin的时候才可以访问-->
                    /permissionTest2 = roles[admin]
                    <!--表示在访问url为signOut时,此时会执行登出logout操作,
                        这里如果不自定义登出过滤器,会默认使用LogoutFilter过滤器来自动完成登出操作,并不需要实现controller层对应的signOut方法
                        也可以自定义登出过滤器,需要继承LogoutFilter过滤器,这里我自定义了登出过滤器
                    -->
                    /signOut = logout
                </value>
            </property>
        </bean>
    
</beans>

  配置完后,记得使用<import resource="classpath:configs/applicationContext-shiro.xml"/>将其导入进spring配置文件中。

  2.自定义实现的Realm

public class MyRealm extends AuthorizingRealm{
    @Resource
    private UserService userService;
    /**
     * 授权
     * 此方法 只有在shiro的配置文件中配置了权限才会进行调用,例如配置了role,permission
     * @param pc
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
        //根据自己系统规则的需要编写获取授权信息,这里为了快速入门只获取了用户对应角色的资源url信息
        //获取当前登录输入的用户名
        String username = (String) pc.fromRealm(getName()).iterator().next();
        if (username != null) {
            User user = null;
            try {
                //根据用户名查询对应的user
                user = userService.getByUserName(username);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //根据用户获取对应的role,根据role获取对应的permission
            Set<Role> roleSet = user.getRoleSet();
            List<String> pers = new ArrayList<>();
            for(Role r:roleSet){
                Set<Permission> permissionSet = r.getPermissionSet();
                for(Permission p:permissionSet){
                    pers.add(p.getUrl());
                }
            }

            if (pers != null && !pers.isEmpty()) {
                SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
                for (String each : pers) {
                    //将权限资源添加到用户信息中  会与配置文件中配置的对应权限做对比
                    info.addStringPermission(each);
                }
                return info;
            }
        }
        return null;
    }

    /**
     * 认证,登录验证
     * @param at
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken at) throws AuthenticationException {
        //获取存进shiro token中的用户信息,(token里的信息在Controller层的login方法里存的)
        UsernamePasswordToken token = (UsernamePasswordToken) at;
        String username = token.getUsername();
        if (username != null && !"".equals(username)) {
            User user = null;
            try {
                //根据用户名去数据库中查询对应的user
                user = userService.getByUserName(username);
                if (user != null) {
                //将数据库中的用户名,密码拿去和存在shiro token中的用户输入的用户名,密码做对比
                return new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), getName());
            }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

  3.自定义实现的LogoutFilter

public class SignOutFilter extends LogoutFilter{
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        Subject subject = getSubject(request,response);
        String redirectUrl = getRedirectUrl(request,response,subject);
        try{
            subject.logout();
        }catch (Exception e){
            e.printStackTrace();
        }
        issueRedirect(request,response,redirectUrl);
        //这里返回false 代表不再去执行controller层的方法,
        // true代表还会去执行(如果需要在登出的controller里自定义一些东西,例如清除session或者cookie的一些信息,
        // 这个时候只需要返回true)
        return true;
    }
}

  4.Controller层的相关方法

@Controller("LoginAction")
public class LoginAction extends BaseAction{
    @Resource
    private UserService userService;
    //接收表单填写的用户名 密码
    private String userName;
    private String password;

    public String login(){
        Subject subject = SecurityUtils.getSubject();
        //判断当前登录用户是否已经被认证,即是否已经登录了,如果已经登录了,再次发送登录的请求,就不再进行认证了
        if(!subject.isAuthenticated()){
            //没有登录  进行登录认证
            //将登录的用户名,密码存进shiro token中
            UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
try{
                //去自己的MyRealm类中执行doGetAuthenticationInfo()方法,进行登录认证
                subject.login(token);
            } catch ( UnknownAccountException e ) {
                System.out.println("用户未注册!");
                return "error";
            } catch ( IncorrectCredentialsException e ) {
                System.out.println("密码错误!!");
                return "error";
            } catch ( LockedAccountException e ) {
                System.out.println("该账户不可用~");
                return "error";
            } catch ( ExcessiveAttemptsException e ) {
                System.out.println("尝试次数超限!!");
                return "error";
            }
            return "success";
        }
        return "success";
    }

    //认证测试
    public void authTest(){
        System.out.println("authTest------进来了");
    }
    //权限授权测试
    public void permissionTest(){
        System.out.println("permissionTest--------进来了");
    }

    //登出测试
    public String signOut(){
        System.out.println("controller---登出---------");
        //实现对session或者其他信息的清除
        return "success";
    }

    @Override
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

  对应的struts.xml中action访问路径以及对应返回资源视图的配置,以及对应的各种jsp页面这里就不再贴代码了。 

 

  测试:

 Shiro快速入门第1张

  在表单中,填写对应的用户名,密码,然后提交,会去访问/login,此访问地址,在shiro配置中为anon,故直接放行,去controller层的login方法,然后执行到subject.login(token);,会去MyRealm中执行doGetAuthenticationInfo()方法,去验证登录信息,验证后再返回到controller层的login方法中继续执行,根据不同的验证结果,返回对应的结果视图。

Shiro快速入门第2张

  登录成功后,在浏览器地址栏输入..../signOut,由于shiro配置文件为logout,则会执行登出操作,这个时候会去自定义SignOutFilter里面执行preHandle()方法,由于此时返回值是true,则还需要去执行controller层的signOut()方法。

Shiro快速入门第3张

  登录成功后,在浏览器地址栏输入.../authTest,由于shiro配置中为authc,则需要登录认证成功后才可以继续放行访问,由于已经登录成功,则可以访问成功,会继续执行controller层的authTest()方法;如果还未登录,在浏览器地址栏输入..../authTest,此时跳转到配置的登录页面。

Shiro快速入门第4张

  登录成功后,在在浏览器地址栏输入.../permissionTest,由于shiro配置中为perms[/permissionTest],则需要permission权限才可以继续访问,由于已经登录成功,则会去MyRealm中的doGetAuthorizationInfo()方法中进行授权认证,若授权成功,会继续执行controller层的permissionTest()方法;如果还未登录,在浏览器地址栏输入..../permissionTest,此时跳转到配置的登录页面。

对于快速入门,要做到下面三个问题,就已经差不多了  

1.什么是shiro?

2.shiro可以用来干嘛?

3.shiro如何使用?

  附上两张斌哥ppt中的图,这样会对认证与授权的整个流程有更清楚的认识:

  身份认证:

Shiro快速入门第5张

  身份授权:

Shiro快速入门第6张

参考资料:

https://blog.csdn.net/u013142781/article/details/50629708?utm_source=blogxgwz0-----Shiro安全框架入门篇(登录验证实例详解与源码)

https://blog.csdn.net/swingpyzf/article/details/46342023/-------Apache Shiro 快速入门教程,shiro 基础教程

https://www.cnblogs.com/Mick-mod/p/8942072.html------ssh框架整合shiro权限

https://blog.csdn.net/shencange/article/details/73289801------使用shiro进行登录密码安全验证

https://blog.csdn.net/chengxuzaza/article/details/72851707------shiro实现系统的退出功能

https://blog.csdn.net/qq_34292044/article/details/79131199------Shiro实现logout操作

 

免责声明:文章转载自《Shiro快速入门》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇websocket 断线重连简单谈谈python的反射机制下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

将对象放入到map中,找出map中指定的某个属性值并放在一个list中

package facadeTest.mapAndObject; import java.util.ArrayList; import java.util.List; /** * * 将对象放入到map中,找出map中指定的某个属性值并放在一个list中 */ public class TargetAndMapChange { public...

【JavaEE】SSH+Spring Security+Spring oauth2整合及example

现在加最后一样,就是oauth2,现在很多网站都有对应的移动版本,那么移动端访问服务端的服务怎么控制权限,我知道的主要是两种方法,第一是模拟浏览器,访问服务的时候会生成session,之后在移动端缓存cookie,每次网络请求都把cookie加上,还有一种就是通过oauth2,登录之后生成一个凭证,每次请求时携带凭证,当然oauth2更多的是为第三方应用提...

VBA 字符串处理

1 VBA中的字符串 2 VBA中处理字符串的函数     2.1 比较字符串     2.2 转换字符串     2.3 创建字符串     2.4 获取字符串的长度     2.5 格式化字符串     2.6 查找字符串     2.7 提取字符/字符串     2.8 删除空格     2.9 返回字符代码     2.10 返回数值代表的相应字符...

使用WMI控制Windows进程 和服务

1.使用WMI控制Windows进程 本文主要介绍两种WMI的进行操作:检查进程是否存在、创建新进行 代码如下: using System; using System.Collections.Generic; using System.Text; using System.Management; using System.Threa...

spring定时任务原理

参考文章:https://juejin.im/post/5b64448af265da0f7f44c201 https://juejin.im/post/5e338ebae51d4558864b1ca0 1、开发中使用时要注意的点 (0)spring定时任务执行原理实际使用的是JDK自带的ScheduledExecutorService (1)spring默...

Android开发中java与javascript交互:PhoneGap插件vs addJavascriptInterface

1.前言 在《用PhoneGap+jQueryMobile开发Android应用实例》中,我们讲到PhoneGap(以下称Cordova)开发环境的搭建,以及如何整合出一个基本的Android应用框架(并给出了范例代码)。于是乎,我们便开始日夜兼程,披星戴月的炮制我们的第一个手机应用了。 但实际上,除了常见的API调用规范(有且仅有自查手册一途)引起的问题...