智能商贸系统05-shiro

1、概念

    Apache Shiro是一个强大且易用的Java安全框架,有身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

  • Spring security 重量级安全框架 A
  • pache Shiro轻量级安全框架

2、四大基石

  • 身份认证(登录) Authentication
  • 授权(权限) Authorization
  • 密码学 Cryptography
  • 会话管理Session Management

除此还有

  • Web Support: Shiro 的 web 支持的 API 能够轻松地帮助保护 Web 应用程序。
  • Caching:缓存是 Apache Shiro 中的第一层公民,来确保安全操作快速而又高效。
  • Concurrency: Apache Shiro 利用它的并发特性来支持多线程应用程序。
  • Testing:测试支持的存在来帮助你编写单元测试和集成测试,并确保你的能够如预期的一样安全。
  • “Run As”:一个允许用户假设为另一个用户身份(如果允许)的功能,有时候在管理脚本很有用。
  • “Remember Me”:在会话中记住用户的身份,所以他们只需要在强制时候登录。

在这里插入图片描述

3、重要的对象

  • Subject:当前用户
  • SecurityManager:权限管理器(所有功能管理)
  • Realm:获取权限数据

在这里插入图片描述

4、测试1—普通maven项目

建一个普通的maven项目

在官方的文件中找到一个samples

4.1、导包
<!--使用shiro需要先导包-->
<dependencies>
    <!--shiro的核心包-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.4.0</version>
    </dependency>
    <!--日志包-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <!--测试包-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.9</version>
    </dependency>
</dependencies>
4.2、ini文件

在官方文档中找到ini文件,截取部分来进行测试。这些用户及权限,真实应该从数据库中获取,这里只是为了测试一些api。
在这里插入图片描述

# -----------------------------------------------------------------------------
#users 用户
#root 用户名,777777  密码,adming  角色
#guest 用户名,888888  密码,guest  角色
#
# -----------------------------------------------------------------------------
[users]
root = 777777, admin
guest = 888888, guest

# -----------------------------------------------------------------------------
[roles]
admin = *
schwartz = employee:*
goodguy = winnebago:drive:eagle5

4.3、ShiroTest测试
public class ShiroTest {
    @Test
    public void testShiro() throws Exception {
        //拿到权限管理工厂,数据源是配置文件内的shiro.ini
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //拿到权限管理器对象
        SecurityManager securityManager = factory.getInstance();
        //将权限管理器放入工具类中,这样所有的权限功能都能实现
        SecurityUtils.setSecurityManager(securityManager);
        //拿到当前用户(当前用户可能是一个游客,不确定是否登录)
        Subject subject = SecurityUtils.getSubject();
        //判断当前用户是否为登录用户,false表示未登录
        boolean authenticated = subject.isAuthenticated();
        System.out.println(authenticated);
        //未登录用户使用令牌登录,令牌为用户名和密码
        if (!authenticated) {
            System.out.println("是否登录:" + subject.isAuthenticated());
            try {
                UsernamePasswordToken root = new UsernamePasswordToken("root", "777777");
                subject.login(root);
                //未知账户异常
            } catch (UnknownAccountException e) {
                System.out.println("账户错误");
                e.printStackTrace();
                //密码错误,错误的凭证
            } catch (AuthenticationException e) {
                //最大的,登录异常
                System.out.println("登录异常");
                e.printStackTrace();
            }
        }
        //判断是否有角色
        System.out.println("是否是admin角色"+subject.hasRole("admin") );
        //判断是否有权限
        System.out.println("是否有employee:index权限"+subject.isPermitted("employee:index"));
        System.out.println("是否有employee:save权限"+subject.isPermitted("employee:save"));
        System.out.println("是否有employee:delete权限"+subject.isPermitted("employee:delete"));
        System.out.println("是否有department:delete权限"+subject.isPermitted("department:delete"));
        subject.logout();
        System.out.println("是否登录:" + subject.isAuthenticated());
    }
}

4.4、Myrealm

自定义realm,这里继承的是AuthorizingRealm这个类,覆写身份验证和权限验证

//自定义realm
public class MyRealm extends AuthorizingRealm {
    //授权功能
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //创建一个授权对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //获取与设置角色
        Set<String> roles = findRoles();
        authorizationInfo.setRoles(roles);
        //拿到与设置权限
        Set<String> perms = findPerms();
        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }

    //查询角色
    private Set<String> findRoles() {
        Set<String> roles = new HashSet<String>();
        roles.add("admin");
        return roles;
    }

    //查询权限
    private Set<String> findPerms() {
        Set<String> perms = new HashSet<String>();
        perms.add("*");
        return perms;
    }

    //登录功能
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //将令牌转换成用户密码令牌
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        //获取到用户
        String username = usernamePasswordToken.getUsername();
        //根据用户名查询密码,如果返回的为空,说明用户名不存在
        String password = findByUsernae(username);
        //判断用户是否存在
        if (password == null) {
            return null;
        }
        //如果用户存在,判断密码是否正确,需要两个密码,一是前台传来的,二是在后台查到的密码
        /**
         * 第一个参数:principal(主体) =》 登录成功后在任何地方都可以拿到的对象
         *      以前开始登录成功我们就把用户放到session中
         * 第二个参数:密码(从数据库查出来的密码)
         * 第三个参数:加盐
         * 第四个参数:当前realm的名称(随便取)
         */
        ByteSource byteSource = ByteSource.Util.bytes("1111");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password,byteSource,getName());
        return authenticationInfo;
    }

    //模拟数据库查询
    public String findByUsernae(String username) {
        if ("admin".equals(username)) {
            return "dfd767e97113b55283c9568c9ef1ab8a";
        } else if ("root".equals(username)) {
            return "777777";
        }
        return null;
    }
}
4.5、Myrealm测试
public class MyRealmTest {
    @Test
    public void testMyRealm() throws Exception {
        //创建权限管理
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        //创建一个realm
        MyRealm myRealm = new MyRealm();
        //设置凭证匹配器,HashedCredentialsMatcher
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //匹配器算法名字
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        //设置迭代次数
        hashedCredentialsMatcher.setHashIterations(10);
        //加盐的方式在密码验证
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);

        //把realm放入权限管理器中
        securityManager.setRealm(myRealm);
        //将权限管理器放入工具中,功能都可以实现
        SecurityUtils.setSecurityManager(securityManager);
        //拿到当前用户
        Subject subject = SecurityUtils.getSubject();
        //验证用户是否为登录
        System.out.println("是否为登录" + subject.isAuthenticated());
        //没有登录就去登录
        if (!subject.isAuthenticated()) {
            //创建登录的令牌
            try {
                AuthenticationToken token = new UsernamePasswordToken("admin", "123456");
                subject.login(token);
                //用户名不存在
            } catch (UnknownAccountException e) {
                System.out.println("用户名不存在");
                e.printStackTrace();
                //密码错误
            } catch (IncorrectCredentialsException e) {
                System.out.println("密码错误");
                e.printStackTrace();
            } catch (AuthenticationException e) {
                System.out.println("高级错误");
                e.printStackTrace();
            }
        }
        System.out.println("是否为admin角色" + subject.hasRole("admin"));
        System.out.println("是否有employee:xxxx的权限" + subject.isPermitted("employee:xxxx"));


        System.out.println("是否为登录" + subject.isAuthenticated());
    }
    /*
    * 密码加密算法
    *       MD5,SHA
    *       algorithmName:算法名称
    *       source:密码
    *       salt:加盐
    *       hashlterations:10次
    * */
    @Test
    public void testHashPassword() throws Exception {
        SimpleHash simpleHash2 = new SimpleHash("MD5", "123456");
        SimpleHash simpleHash = new SimpleHash("MD5","123456","1111",10);

        System.out.println(simpleHash.toHex());
    }
}

5、测试2----mavenweb项目/集成Spring

5.1、导包
<!-- shiro(权限框架)的支持包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-all</artifactId>
  <version>1.4.0</version>
  <type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
</dependency>
5.2、web.xml中配置代理过滤器
   <!--shiro过滤器
    Delegating(授权)Filter(过滤器)Proxy(代理)
    只拦截其他不做
    -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <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>
5.3、自定义realm(暂时不从数据库中获取)
//自定义realm
public class AisellRealm extends AuthorizingRealm {
    //授权功能
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //创建一个授权对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //获取与设置角色
        Set<String> roles = findRoles();
        authorizationInfo.setRoles(roles);
        //拿到与设置权限
        Set<String> perms = findPerms();
        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }

    //查询角色
    private Set<String> findRoles() {
        Set<String> roles = new HashSet<String>();
        roles.add("admin");
        return roles;
    }

    //查询权限
    private Set<String> findPerms() {
        Set<String> perms = new HashSet<String>();
        perms.add("employee:*");
        return perms;
    }

    //登录功能
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //将令牌转换成用户密码令牌
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        //获取到用户
        String username = usernamePasswordToken.getUsername();
        //根据用户名查询密码,如果返回的为空,说明用户名不存在
        String password = findByUsernae(username);
        //判断用户是否存在
        if (password == null) {
            return null;
        }
        //如果用户存在,判断密码是否正确,需要两个密码,一是前台传来的,二是在后台查到的密码
        /**
         * 第一个参数:principal(主体) =》 登录成功后在任何地方都可以拿到的对象
         *      以前开始登录成功我们就把用户放到session中
         * 第二个参数:密码(从数据库查出来的密码)
         * 第三个参数:加盐
         * 第四个参数:当前realm的名称(随便取)
         */
        ByteSource byteSource = ByteSource.Util.bytes("1111");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password,byteSource,getName());
        return authenticationInfo;
    }

    //模拟数据库查询
    public String findByUsernae(String username) {
        if ("admin".equals(username)) {
            return "dfd767e97113b55283c9568c9ef1ab8a";
        } else if ("root".equals(username)) {
            return "777777";
        }
        return null;
    }
}

5.3、准备工厂返回权限FilterChainDefinitionMapFactory

这个工厂,主要是解决在applicationContext-shiro.xml中配置权限的局限性,将权限写在java代码中,方面修改。

  • 返回的Map值是有顺序的
  • 修改后要重启(热启动无效)
package com.xuxusheng.aisell.web.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

public class FilterChainDefinitionMapFactory {
    /*
    *
    *  /login = anon
      /s/permission.jsp = perms[user:index]
     /** = authc
    * */
    public Map<String, String> createMap() {
        Map<String, String> map = new LinkedHashMap<>();
        //一定注意顺序
        //不拦截的
        map.put("/login","anon");
        //设置权限
        map.put("/employee/index","perms[employee:index]");
        map.put("/department/index","perms[department:index]");
        //全部拦截
        map.put("/** ","authc");
        return map;
    }
}

5.4、applicationContext-shiro.xml的配置

注意:先在 applicationContext.xml 中引入它
< import resource=“classpath: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-3.0.xsd">
    <!--
        Shiro的核心对象(权限管理器)
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(jpaRealm)
    -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="aisellRealm"/>
    </bean>
    <!--
        JpaRealm jpaRealm = new JpaRealm();
        配置咱们的自定义realm
     -->
    <bean id="aisellRealm" class="com.xuxusheng.aisell.web.shiro.AisellRealm">
        <!--Realm的名称-->
        <property name="name" value="aisellRealm"/>
        <property name="credentialsMatcher">
            <!-- 配置哈希密码匹配器 -->
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--加密方式:MD5-->
                <property name="hashAlgorithmName" value="MD5"/>
                <!--迭代次数-->
                <property name="hashIterations" value="10" />
            </bean>
        </property>
    </bean>
    <!-- 这三个配置好,可以支持注解权限 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
    <!--
        shiro真正的过滤器(功能就是它完成的)
            这个bean的名称必需和web.xml里的的代理过滤器名字相同
     -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--必需要用到权限管理器-->
        <property name="securityManager" ref="securityManager"/>
        <!--如果你没有登录,你会进入这个页面-->
        <property name="loginUrl" value="/s/login.jsp"/>
        <!--登录成功后,进入的页面(一般没什么用)-->
        <property name="successUrl" value="/s/main.jsp"/>
        <!--如果你没有权限,你会进入这个页面-->
        <property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
        <!-- 过滤描述
            anon:不需要登录也可以访问
            authc:登录与权限的拦截
            perms:如果你有user:index的权限,你就可以访问:/s/permission.jsp
        -->
        <!--
        <property name="filterChainDefinitions">
            <value>
                /login = anon
                /s/permission.jsp = perms[user:index]
                /** = authc
            </value>
        </property>
        -->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap" />
    </bean>
    <!--
        以前在四个创建Bean的方法中讲过
        ShiroFilterMapFactory shiroFilterMapFactory = new shiroFilterMapFactory();
        Map filterChainDefinitionMap=shiroFilterMapFactory.createMap();
    -->
    <!--拿到shiroFilterMapFactory里面的createMap方法的值 -->
    <bean id="filterChainDefinitionMap" factory-bean="shiroFilterMapFactory" factory-method="createMap" />
    <!--配置返回shiro权限拦截的bean-->
    <bean id="shiroFilterMapFactory" class="com.xuxusheng.aisell.web.shiro.FilterChainDefinitionMapFactory"/>
</beans>

更多详细的介绍参考链接
【让 Apache Shiro 保护你的应用】https://www.infoq.cn/article/apache-shiro/?itm_source=infoq_en&itm_medium=link_on_en_item&itm_campaign=item_in_other_langs

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值