shiro安全框架学习

shiro框架

1.简介

Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。

Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。

Shiro能到底能做些什么呢?

  • 验证用户身份
  • 用户访问权限控制,比如:1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限
  • 在非 Web 或 EJB 容器的环境下可以任意使用Session API
  • 可以响应认证、访问控制,或者 Session 生命周期中发生的事件
  • 可将一个或以上用户安全数据源数据组合成一个复合的用户 “view”(视图)
  • 支持单点登录(SSO)功能
  • 支持提供“Remember Me”服务,获取用户关联信息而无需登录

为什么是 Shiro

使用 Shiro 官方给了许多令人信服的原因,因为 Shiro 具有以下几个特点:

  • 易于使用——易用性是项目的最终目标。应用程序安全非常令人困惑和沮丧,被认为是“不可避免的灾难”。如果你让它简化到新手都可以使用它,它就将不再是一种痛苦了。
  • 全面——没有其他安全框架的宽度范围可以同Apache Shiro一样,它可以成为你的“一站式”为您的安全需求提供保障。
  • 灵活——Apache Shiro可以在任何应用程序环境中工作。虽然在网络工作、EJB和IoC环境中可能并不需要它。但Shiro的授权也没有任何规范,甚至没有许多依赖关系。
  • Web支持——Apache Shiro拥有令人兴奋的web应用程序支持,允许您基于应用程序的url创建灵活的安全策略和网络协议(例如REST),同时还提供一组JSP库控制页面输出。
  • 低耦合——Shiro干净的API和设计模式使它容易与许多其他框架和应用程序集成。你会看到Shiro无缝地集成Spring这样的框架, 以及Grails, Wicket, Tapestry, Mule, Apache Camel, Vaadin…等。
  • 被广泛支持——Apache Shiro是Apache软件基金会的一部分。项目开发和用户组都有友好的网民愿意帮助。这样的商业公司如果需要Katasoft还提供专业的支持和服务。

基本功能

在这里插入图片描述

2.基本使用

环境准备

shiro不依赖容器,直接创建maven工程即可

pom.xml依赖

 <dependency>
     <groupId>org.apache.shiro</groupId>
     <artifactId>shiro-core</artifactId>
     <version>1.10.1</version>
</dependency>

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

创建ini文件

shiro获取权限信息可以通过数据库获取,也可以通过ini配置文件获取

[users]
zhangsan=z3
lisi=l4

登入认证概念

  • 身份验证:一般需要提供身份id等一些标识信息来表明登录者的身份,如提供email,用户名/密码来证明

  • 再shiro中,用户选哟提供principals(身份)和credentials(证明)给shiro,从而应用能验证用户身份

  • principals:身份,即主题的标识属性,可以是任何属性,如果用户,邮箱等,唯一即可,一个主题可有多个principals,但只有一个primary principals,一般是用户名/邮箱/手机号

  • credentials:证明,凭证,即只有主体才知道的安全值,如密码/数字证书等

  • 最常见的principals和credentials组合就是用户名/密码

登录认证基本流程

  1. 收集用户身份/凭证,即如用户名/密码

  2. 调用subject.login进行登录,如果失败将得到相应的AuthenticationException异常,根据异常提示用户错误信息,否则登录成功

  3. 创建自定义Realm的类,继承org.apche.shiro.realm.AuthenticatingRealm类,实现doGetAuthenticationInfo()方法

基础代码

public static void main(String[] args) {
    //1.初始化获取SecurityManager
    BasicIniEnvironment environment = new BasicIniEnvironment("classpath:shiro.ini");
    SecurityManager securityManager = environment.getSecurityManager();
    SecurityUtils.setSecurityManager(securityManager);
    //2获取Subject对象
    Subject subject = SecurityUtils.getSubject();
    //3创建token对象
    AuthenticationToken token = new UsernamePasswordToken("zhangsan", "z3");

    try {
        //4完成登录
        subject.login(token);
        System.out.println(token.toString());
    } catch (IncorrectCredentialsException e) {
        e.printStackTrace();
        System.out.println("密码错误");
    } catch (UnknownAccountException e) {
        e.printStackTrace();
        System.out.println("用户错误");
    }
}

授权

授权概念
1678203555608
  • 授权,也叫访问控制,即在应用中控制谁访问哪些资源
  • 主体(Subject),访问应用的用户,再shiro中使用Subject代表该用户,用户只有授权后才能访问相应的资源
  • 资源(Resource),再应用中用户可以访问的URL
  • 权限(Premission),安全策略中的原子授权单位,通过权限我们可以表示再应用中用户有没有操作某个资源的权力
  • shiro支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)
  • 角色(Role),权限的集合,一般情况下会赋予用户角色而不是权限,不同角色拥有一组不同的权限
授权方式
  1. 编程式:通过if/else代码块完成
  2. 注解式:通过再执行的方法上放置相应的注解完成,没有权限将抛出相应的异常
  3. JSP/GSP 标签:在 JSP/GSP 页面通过相应的标签完成
授权

在 ini 配置文件配置用户拥有的角色

[users]
zhangsan=z3,role1,role2
lisi=l4

hasRole/hasRoles;用于判断用户是否拥有某个角色/某些权限

boolean role = subject.hasRole("role1");
subject.hasRoles(Arrays.asList("role1", "role2"));

在 ini 配置文件配置用户拥有的角色及角色-权限关系

[users]
zhangsan=z3,role1,role2
lisi=l4

[roles]
role1=user:create,user:update
role2=user:create,user:delete

Shiro 提供了 isPermitted 和 isPermittedAll 用于判断用户是否拥有某个权限或所有权限

System.out.println(subject.isPermitted("user:create"));

shiro加密

实际系统开发中,一些敏感信息需要进行加密,比如用户的密码,shiro内嵌很多常用的加密算法,如果MD5加密,shiro可以很简单的使用信息加密。

 public static void main(String[] args) {
     String secret = "x11";
     Md5Hash md5Hash = new Md5Hash(secret);
     System.out.println(md5Hash);

     // 使用盐值加密
     Md5Hash md5Hash1 = new Md5Hash(secret, "java");
     // salt + 加密次数
     Md5Hash md5Hash2 = new Md5Hash(secret, "salt", 3);

     System.out.println(md5Hash2);


     System.out.println(md5Hash1);
 }
public class Md5Hash extends SimpleHash
    
public static final String ALGORITHM_NAME = "MD5";

public Md5Hash() {
    super("MD5");
}

public Md5Hash(Object source) {
    super("MD5", source);
}

public Md5Hash(Object source, Object salt) {
    super("MD5", source, salt);
}

public Md5Hash(Object source, Object salt, int hashIterations) {
    super("MD5", source, salt, hashIterations);
}

Md5hash是通过super,将参数传递给父类SimpleHash,所有也可以直接通过 SimpleHash指定加密方法

SimpleHash simpleHash = new SimpleHash("Md5", secret, "java", 3);

3.springboot整合shiro

依赖

<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.10.0</version>
</dependency>

自定义Realm类

继承AuthorizingRealm类,重写doGetAuthorizationInfodoGetAuthenticationInfo方法

public class UserRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

     // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username = "xin";
        String password = "12345";

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        if (!token.getUsername().equals(username)) {
            return null;
        }

        // 密码认证
        return new SimpleAuthenticationInfo("", password, "");
    }
}

创建配置类

@Configuration
public class ShiroConfig {

    // 创建ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier(value = "securityManager") DefaultSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);

        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put("/user/update", "authc");

        bean.setFilterChainDefinitionMap(map);

        return bean;
    }

    // 创建DefaultSecurityManager
    @Bean("securityManager")
    public DefaultSecurityManager getDefaultSecurityManager(@Qualifier(value = "userRealm") UserRealm userRealm) {
//        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    // 创建realm对象
    @Bean("userRealm")
    public UserRealm userRealm() {
        return new UserRealm();
    }

}

setLoginUrl(String loginUrl)`设置登录页

setFilterChainDefinitionMap传入map集合过滤需要验证的页面

创建DefaultSecurityManager,使用的类是 DefaultWebSecurityManager

控制器登入方法

 @RequestMapping("/login")
public String login(String username, String password, Model model) {

    // 获取当前用户
    Subject subject = SecurityUtils.getSubject();
    // 封装用户数据
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    // 执行登入方法,有异常抛出异常
    try {
        subject.login(token);
        return "index";
    } catch (UnknownAccountException e) {
        model.addAttribute("msg", "用户信息错误");
        return "login";
    } catch (IncorrectCredentialsException e) {
        model.addAttribute("msg", "密码错误");
        return "login";
    }
}

subject.login()会调用自定义Realm中的 doGetAuthenticationInfo方法

测试

拦截成功,无设置登入界面

在这里插入图片描述

没有得到授权

在这里插入图片描述

整合mybatis

验证

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    User user = userMapper.getOne(token.getUsername(), String.valueOf(token.getPassword()));
    if (user == null) {
        return null;
    }
    return new SimpleAuthenticationInfo("", user.getPassword(), "");
}

授权

//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    info.addStringPermission("user:add");

    return info;
}
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("user:add");
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();
        info.addStringPermission(currentUser.getPerms);

        return info;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        User user = userMapper.getOne(token.getUsername(), String.valueOf(token.getPassword()));
        if (user == null) {
            return null;
        }
        return new SimpleAuthenticationInfo(user, user.getPassword(), "");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值