zoukankan      html  css  js  c++  java
  • Shiro

    https://www.sojson.com/shiro

    Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。

    Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;

    Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

    Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;

    Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

    Web Support:Web 支持,可以非常容易的集成到 Web 环境;

    Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;

    Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

    Testing:提供测试支持;

    Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

    Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

    应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject;其每个 API 的含义:

    Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

    SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

    Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

    也就是说对于我们而言,最简单的一个 Shiro 应用:

    1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;

    2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

    从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。

    Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;

    SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。

    Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

    Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

    Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

    SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);

    SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;

    CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

    Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。

    ssm整合shiro安全框架的步骤:

    1、引入shiro安全框架的所需jar包

     

    <!-- shiro -->
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.3</version>
    </dependency>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.2.3</version>
    </dependency>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.2.3</version>
    </dependency>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.2.3</version>
    </dependency>

    2、在web.xml文件中配置shiro拦截器

    <!-- spring整合安全框架 -->
    <filter>
    <filter-name>DelegatingFilterProxy</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <!-- 初始化参数 -->
    <init-param>
    <param-name>targetBeanName</param-name>
    <param-value>shiroFilter</param-value>
    </init-param>
    </filter>
    <filter-mapping>
    <filter-name>DelegatingFilterProxy</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

    3、创建spring整合shiro安全框架的配置文件applicationContext-shiro.xml

      <!-- shiro开启事务注解 -->
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager" />
        </bean>
        
        <!-- 除了已经设置的其他路径的认证-->
        <!-- shiro工厂bean配置 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!-- shiro的核心安全接口 -->
            <property name="securityManager" ref="securityManager"></property>
            <!-- 要求登录时的连接 -->
            <property name="loginUrl" value="/login.jsp"></property>
            <!-- 登录成功后要跳转的连接(此处已经在登录中处理了) -->
            <!-- <property name="successUrl" value="/index.jsp"></property> -->
            <!-- 未认证时要跳转的连接 -->
            <property name="unauthorizedUrl" value="/refuse.jsp"></property>
            <!-- shiro连接约束配置 -->
            <property name="filterChainDefinitions">
                <value>
                    <!-- 对静态资源设置允许匿名访问 -->
                    /images/** = anon
                    /js/** = anon
                    /css/** = anon
                    <!-- 可匿名访问路径,例如:验证码、登录连接、退出连接等 -->
                    /auth/login = anon
                    <!-- 剩余其他路径,必须认证通过才可以访问 -->
                    /** = authc
                </value>
            </property>
        </bean>
        
        <!-- 配置shiro安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realms" ref="customRealm"></property>
        </bean>
        
        <!-- 自定义Realm -->
        <bean id="customRealm" class="com.zxz.auth.realm.UserRealm">
            <property name="credentialsMatcher" ref="credentialsMatcher"></property>
        </bean>
        
        <!-- 配置凭证算法匹配器 -->
        <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <!-- Md5算法 -->
            <property name="hashAlgorithmName" value="Md5"></property>

          <!-- 进行1次加密 -->
          <property name="hashIterations" value="2" />
          <property name="storedCredentialsHexEncoded" value="false" />

        </bean>

    【高能说明:以上俩个配置文件中加粗画线的红色部分必须一致,没毛病。】

    4、当然,在这之前,还要编写自定义realm类,该类必须认AuthorizingRealm类做爸爸,不然你是不行滴,之后还有俩个儿子需要处理了,一个是认证另一个授权

    public class UserRealm extends AuthorizingRealm {
        
        @Autowired
        private UserService userService;
        
        @Override
        public String getName() {
            return "customRealm";
        }
        
        /**
         * 认证
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            // 获取用户名称
            String username = (String) token.getPrincipal();
            User user = userService.findByUsername(username);
            if (user == null) {
                // 用户名不存在抛出异常
                System.out.println("认证:当前登录的用户不存在");
                throw new UnknownAccountException();
            }
            String pwd = user.getPassword();
            return new SimpleAuthenticationInfo(user, pwd, getName());
        }
    
        /**
         * 授权
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection princ) {
            return null;
        }
    }

    5、到这儿,shiro安全框架的整合是完成了,然后编写action类来实现登录功能

    /**
         * shiro框架登录
         * @param user
         */
        @RequestMapping(value = "/login",method=RequestMethod.POST)
        public ModelAndView login(User user){
            // 表面校验
            if(!StringUtil.isNullOrBlank(user.getUsername()) || !StringUtil.isNullOrBlank(user.getPassword())){
                 return new ModelAndView("login")
                         .addObject("message", "账号或密码不能为空")
                         .addObject("failuser", user);
            }
            // 获取主体
            Subject subject = SecurityUtils.getSubject();
            try{
                // 调用安全认证框架的登录方法
                subject.login(new UsernamePasswordToken(user.getUsername(), user.getPassword()));
            }catch(AuthenticationException ex){
                System.out.println("登陆失败: " + ex.getMessage());
                return new ModelAndView("login")
                        .addObject("message", "用户不存在")
                        .addObject("failuser", user);
            }
            // 登录成功后重定向到首页
            return new ModelAndView("redirect:/index");
        }

    当某用户登录成功之后,shiro安全框架就会将用户的信息存放在session中,你可以通过User user = (User) SecurityUtils.getSubject().getPrincipal();这句代码在任何地方任何时候都能获取当前登录成功的用户信息.

    Shiro 标签(jsp和Freemarker)的使用。

    在jsp中,在使用Shiro标签库前,首先需要在JSP引入shiro标签:
    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

    1.guest(游客) :验证当前用户是否为“访客”,即未认证(包含未记住)的用户

    jsp: 
    <shiro:guest> Hi there! Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today! </shiro:guest>
    Freemarker:
    <@shiro.guest>
    欢迎[<@shiro.principal/>]登录,<a href="/logout.shtml">退出</a>
    </@shiro.guest>

    以下类似, 使用jsp时标签为 <shiro:**></shiro:**>,而freemarker时<@shiro.**></@shiro.**>,注意:一个冒号一个点

    2.user(已经登录,或者记住我登录)

    <shiro:user> 
     
        Welcome back John!  Not John? Click <a href="login.jsp">here<a> to login. 
     
    </shiro:user>

    3.authenticated(已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在)

    <shiro:authenticated> 
     
        <a href="updateAccount.jsp">Update your contact information</a>. 
     
    </shiro:authenticated>

    4.notAuthenticated(未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户)

    <shiro:notAuthenticated> 
     
        Please <a href="login.jsp">login</a> in order to update your credit card information. 
     
    </shiro:notAuthenticated>
    这个功能主要用途,识别是不是本次操作登录过的,比如支付系统,进入系统可以用记住我的登录信息,但是当要关键操作的时候,需要进行认证识别。

    5.principal标签(输出当前用户信息,通常为登录帐号信息)


    principal标签,取值取的是你登录的时候。在Realm实现类中的如下代码:
      return new SimpleAuthenticationInfo(user,user.getPswd(), getName());
    在new SimpleAuthenticationInfo(第一个参数,....)的第一个参数放的如果是一个username,那么就可以直接用。
    1. <!--取到username-->
    2. <@shiro. principal/>
    如果第一个参数放的是对象,比如我喜欢放User对象。那么如果要取username字段。
    1. <!--需要指定property-->
    2. <@shiro.principal property="username"/>
    和Java如下Java代码一致
    1. User user = (User)SecurityUtils.getSubject().getPrincipals();
    2. String username = user.getUsername();
    像我项目中用法:

    <@shiro.user>
    <p>欢迎您: <a class="userName"><@shiro.principal type="com.zy.common.sys.entity.SysUser" property="userName" /></a>
    </@shiro.user>

    6.hasRole标签(判断是否拥有这个角色)

    <@shiro.hasRole name="admin">
    用户[<@shiro.principal/>]拥有角色admin<br/>
    </@shiro.hasRole>

    7.hasAnyRoles标签(判断是否拥有这些角色的其中一个)

    <@shiro.hasAnyRoles name="admin,user,member">
    用户[<@shiro.principal/>]拥有角色admin或user或member<br/>
    </@shiro.hasAnyRoles>

    8.lacksRole标签(判断是否不拥有这个角色)

    <@shiro.lacksRole name="admin">
    用户[<@shiro.principal/>]不拥有admin角色
    </@shiro.lacksRole>

    9.hasPermission标签(判断是否有拥有这个权限)

    <@shiro.hasPermission name="user:add">
    用户[<@shiro.principal/>]拥有user:add权限
    </@shiro.hasPermission>

    10.lacksPermission标签(与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过)

    <@shiro.lacksPermission name="user:add">
    用户[<@shiro.principal/>]不拥有user:add权限
    </@shiro.lacksPermission>

    原文: https://www.sojson.com/blog/143.html

  • 相关阅读:
    LeetCode Path Sum II
    LeetCode Longest Palindromic Substring
    LeetCode Populating Next Right Pointers in Each Node II
    LeetCode Best Time to Buy and Sell Stock III
    LeetCode Binary Tree Maximum Path Sum
    LeetCode Find Peak Element
    LeetCode Maximum Product Subarray
    LeetCode Intersection of Two Linked Lists
    一天一个设计模式(1)——工厂模式
    PHP迭代器 Iterator
  • 原文地址:https://www.cnblogs.com/gaomanito/p/8889338.html
Copyright © 2011-2022 走看看