shiro原理简析+基于springboot基础实践

2021-04-29  本文已影响0人  灿烂的GL

1、shiro原理简析

架构.png
原理简析:
1、subject支持不通调用获取用户信息
2、SecurityManager安全管理器继承了Authenticator(认证)、Authorizer(授权)、SessionManager、cacheManager(缓存)等,realms域支持不同的数据库做具体授权。
3、密码生成器通过cryptography

1、基础方法认证源码分析


目录结构.png

主要思路:
1、从配置文件获取设置的用户密码
2、校验输入的用户密码
3、验证结果输出
shiro的配置文件是ini类型的文件,简单定义几个账号密码,放在resource文件夹下
格式:# user1 = password, role1, role2, ...

[users]
xiaojin=666
lili=777
qiqi=888

测试代码

public class TestAuthenticator {
    public static  void main(String[] arg){
        //1、创建安全管理器对象
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        //2、给安全管理器设置realm
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

        //3、工具类设置安全管理器
        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("xiaoshi","888");
        try{
            subject.login(token);
            System.out.println("认证成功");
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("不知名用户");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
        }
    }

用户认证校验是从login开始的,梳理下用户名认证流程如下:


认证流程.png

可以看出想自定义认证的化可以重写最后两个方法,其中SimpleAccountRealm:doGetAuthenticationInfo方法用来校验账号,密码是shiro自动认证的不需要我们修改

依赖关系.png
小结:AuthenticatingRealm认证realm 有doGetAuthenticationInfo方法;
AuthorizingRealm授权relam有doGetAuthorizationInfo方法;SimpleAccountRealm覆盖了这两个方法

2、spring实现认证
认证的基本思想:
1、通过过滤器拦截请求,获取用户信息
2、匹配数据库中认证/权限信息,可以添加其他功能,比如:密码添加加密认证信息等。
3、如果内容正确,允许访问,否则重新尝试验证或阻止访问
下面是重写的配置文件,

@Configuration
public class ShiroConfig {

    //1、filter
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        Map<String,String> map = new HashMap<>();
        //此类请求不拦截
        map.put("/test/login","anon");
         //此类请求需要鉴权
        map.put("/test/logout","authc")
        //通配符可以节省代码量
        map.put("/sysUser/**","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

    //2、创建安全管理器
    @Bean
    public DefaultWebSecurityManager getDefauleWebSecurity( @Qualifier("realm") Realm realm){
        //上文说的SecurityManager在spring里是DefaultWebSecurityManager 
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    //3、创建自定义realm
    @Bean
    @Qualifier("realm")
    public Realm getRealm(){
        CustomerRealm customerRealm = new CustomerRealm();
//        //修改凭证校验匹配器
//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//        //添加散列
//        hashedCredentialsMatcher.setHashIterations(1024);
//        customerRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return customerRealm;
    }
}

对于授权来说用户和角色,角色和授权之间都是多对多的关系,权限下边又会分很多子粒度,重写授权和认证方法如下:

public class CustomerRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String primaryPrincipal = (String) principals.getPrimaryPrincipal();
      //根据身份信息获取角色以及权限信息,这里的权限信息实际应该是数据库查询信息
        if("lili".equals(primaryPrincipal)){
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //这里手动设置的用户具有admin权限,具体权限显示前端可以做角色控制(可以从数据库中查)
            simpleAuthorizationInfo.addRole("user");
            //权限设置:user角色可操作user:*:*粒度权限,需要结合前端标签做权限控制(可以从数据库中查)
            simpleAuthorizationInfo.addStringPermission("user:*:*");
            return simpleAuthorizationInfo;
        }
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
       String principal =  (String) token.getPrincipal();
        //根据身份信息获取密码信息,这里的用户信息实际应该是数据库查询信息
       if("lili".equals(principal)){
           return new SimpleAuthenticationInfo(principal ,"123",this.getName());
       }
        return null;
    }
}

1、Principals(身份) 是Subject的“标识属性”,可以是任何与Subject相关的标识,比如说名称(给定名称)、名字(姓或者昵称)、用户名、安全号码等等,当然像昵称这样的内容不能很好的对Subject进行独特标识,所以最好的身份信息(Principals)是使用在程序中唯一的标识–典型的使用用户名或邮件地址。
Primary Principal(最主要的身份)虽然 Shiro 可以使用任何数量的身份,Shiro 还是希望一个程序精确地使用一个主要的身份–一个仅有的唯一标识 Subject 值。在多数程序中经常会是一个用户名、邮件地址或者全局唯一的用户 ID。
2、Credentials(证明) 通常是只有 Subject 知道的机密内容,用来证明他们真正拥有所需的身份,一些简单的证书例子如密码、指纹、眼底扫描和X.509证书等。

前端的权限控制,需要把html文件放在static文件夹里(用的是>2.0.3版本的spring,发现不支持jsp了,需要使用html文件)

<html lang="en" xmlns:th="http://www.thymeleaf.org"
    xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="utf-8" name="viewport"
          content="width=device-width, initial-scale=1.0" http-equiv = "X-UA-Compatible" content ="ie=edge" />
    <title>Docment</title>
</head>
<body>
    <h1>系统主页V1.0</h1>
    <!--<a href =${pageContext.request.contextPath}/test/login"> 退出用户</a>-->
    <u1>
        <shiro:hasAnyRoles name="admin,user">
        <li><a href="" >用户管理</a></li>
        <ul>
            <shiro:hasPermission name=" user:add:*">
                <li><a href="">添加</a> </li>
            </shiro:hasPermission>
            <shiro:hasPermission name=" user:delete:*">
                <li><a href="">删除</a> </li>
            </shiro:hasPermission>
            <shiro:hasPermission name=" user:update:*">
                <li><a href="">更新</a> </li>
            </shiro:hasPermission>
        </ul>
        </shiro:hasAnyRoles>
        <shiro:hasRole name="admin">
            <li><a href="" >订单管理</a></li>
            <li><a href="" >商品管理</a></li>
            <li><a href="" >物流管理</a></li>
        </shiro:hasRole>
    </u1>
</body>
</html>

登录文件:

<html lang="en">
<head>
    <meta charset="utf-8" name="viewport"
          content="width=device-width, initial-scale=1.0" http-equiv = "X-UA-Compatible" content ="ie=edge" />
    <title>Docment</title>
</head>
<body>
<h1>用户登录</h1>
<form action="./index.html" >
    用户名:<input type="text" name="username"> <br/>
    密码:<input type="text" name ="password"><br/>
    <button type="submit">登录</button><button type="reset">取消</button>
</form>
</body>
</html>

此外还可以通过注解的方式进行角色校验 @RequiresRoles("角色"),如果是多个角色即 @RequiresRoles("角色1","角色2"),需要注意的是这里的意思是拥有角色1或者角色2的权限,CustomerRealm里也需要进行相应配置; 权限校验 @RequiresPermissions("user::")


3、缓存和过滤器
缓存支持:
1、ehcache管理
2、Redis集中管理

      <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.5.3</version>
        </dependency>
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

设置ehcache缓存

    //3、创建自定义realm
    @Bean
    @Qualifier("realm")
    public Realm getRealm(){
        CustomerRealm customerRealm = new CustomerRealm();
        //修改凭证校验匹配器
//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//        //添加散列
//        hashedCredentialsMatcher.setHashIterations(1024);
//        customerRealm.setCredentialsMatcher(hashedCredentialsMatcher);

        customerRealm.setCacheManager(new EhCacheManager());
        customerRealm.setCachingEnabled(true);
        customerRealm.setAuthenticationCachingEnabled(true);
        customerRealm.setAuthorizationCachingEnabled(true);
        return customerRealm;
    }

EhCacheManager继承了CacheManager返回的是Cache<K, V> ,如果想用redis缓存也可以继承CacheManager,定义redis的Cache<K, V>
持续更新中...


参考链接
1、官网十分钟入门-详情可以自己点点doc
2、授权

上一篇下一篇

猜你喜欢

热点阅读