Shiro基操

本文详细介绍了Apache Shiro的使用,包括Shiro的基本API、工作原理和 Realm 的实现。通过一个简单的 Realm 示例展示了如何进行身份认证,并讲解了如何将Shiro集成到SpringBoot应用中,包括配置类的编写和过滤器链的定义。
摘要由CSDN通过智能技术生成

Shiro简介

Apache Shiro 是 Java 的一个轻量级安全框架。虽说没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个需要根据业务需求来决定 。

Shiro的基本API


上图为Shiro的基本功能模块

  • Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;
  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
  • Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
  • Web Support:Web 支持,可以非常容易的集成到 Web 环境;
    Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率:
  • Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
  • Testing:提供测试支持;
  • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
  • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
    注意:Shiro 不会去维护用户、维护权限;需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 。

Shiro的工作原理

在这里插入图片描述
可以看到:应用代码直接交互的对象是 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 让开发人员自己注入

编写一个简单的Realm (未集成SpringBoot)

从上文中可以知道,shiro所提供的安全保障是基于 Realm 所提供的数据支持 , 现在我们来简单配置一个自己的Realm , 本文采用阿里巴巴的 Druid-1.1.22 数据源管理

一、新建Shiro配置信息(shiro.ini)

#声明一个自定义的的realm
customRealm=com.woniuxy.framework.component.MyRealm
#将realm注入到securityManager中
securityManager.realms=$customRealm

创建Realm类 (MyRealm.java)

这里做一个简单的登录验证

//自定义Realm类需要实现Shiro接口 AuthorizingRealm 
public class MyRealm extends AuthorizingRealm {
	//建立数据源
    private static JdbcTemplate jdbcTemplate = null;
    static{
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/edu");
        dataSource.setUsername("root");
        dataSource.setPassword("123321");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        jdbcTemplate =  new JdbcTemplate(dataSource);
    }
     @Override
     //这是授权验证功能
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    @Override
    //AuthenticationToken:认证令牌,认证入参
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("进入获取认证信息方法:"+token);
        //获取到用户名
        Object username = token.getPrincipal();

        //在数据库中查询用户是否存在
        List<User> userList = jdbcTemplate.query(
                "select * from edu_user where username=?",  //sql
                new BeanPropertyRowMapper<User>(User.class), //结果映射器,将ResultSet中的内容自动映射到User类上
                username);//参数

        //判断结果
        if(userList.size()==0){
            throw new UnknownAccountException(username+"用户不存在");
        }
        if(userList.size()>1){
            throw new AccountException("系统异常,找到多个用户"+username);
        }

        //适配返回对象, 用户、密码、Realm名字
        return new SimpleAuthenticationInfo(userList.get(0),userList.get(0).getPassword(),MyRealm.class.getName());
    }
}

三、进行测试

	 //加载配置文件,创建工厂类
        IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //创建安全管理器
        SecurityManager securityManager = iniSecurityManagerFactory.getInstance();
        //将安全管理器设置到SecurityUtils中
        SecurityUtils.setSecurityManager(securityManager);
        //获取Subject对象,代表一个用户
        Subject subject = SecurityUtils.getSubject();

        //认证
        subject.login(new UsernamePasswordToken("username","password"));
		
        System.out.println("确认验证是否通过"+subject.isAuthenticated());

在这里插入图片描述
这是Subject源码中关于subject.login的描述 , 如果验证失败则会报错 , 可以通过try…catch处理

集成SpringBoot

需要导入 shiro-spring-boot-starter
本次不考虑实现前后端分离 , 简单的写一个静态登录页面即可

一、修改配置选项

1) 新建一个配置类
@Configuration
public class SecurityConfig {

    @Bean
    public Realm shiroRealm(){
        return new ShiroRealm();
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition dsfcd = new DefaultShiroFilterChainDefinition();
        //anon为shiro自带的过滤器名称
        /**
        * anon:无需认证即可访问
        * authc:需要认证才可访问
        * user:点击“记住我”功能可访问
        * perms: 拥有权限才可以访问
        * role: 拥有某个角色权限才能访问
        */
        dsfcd.addPathDefinition("/","anon");
        dsfcd.addPathDefinition("/login","anon");
        dsfcd.addPathDefinition("/login.html","anon");
        //登出
        dsfcd.addPathDefinition("/logout","logout");
        //其他则需要认证
        dsfcd.addPathDefinition("/**","user");
        return dsfcd;
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();

        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,导致返回404。
         * 加入这项配置能解决这个bug
         */
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }
}
2)在SpringBoot主配置文件中添加(本文使用properties格式文件)
shiro.loginUrl=/login.html

二、实现ShiroRealm

// 将上文Realm内容中的查询语句用Mybatis等重新编写一下即可 , 本文采用Mybatis-plus
public class MyRealm extends AuthorizingRealm {
	@Resource
	//用户数据调用
    private UserMapper userMapper;

    @Resource
    //角色数据调用
    private RoleMapper roleMapper;

    @Resource
	//权限数据调用
    private PermMapper permMapper;
     @Override
     //这是授权验证功能
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    @Override
    //AuthenticationToken:认证令牌,认证入参
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("进入获取认证信息方法:"+token);
        //获取用户名
        Object username = token.getPrincipal();
        //获取密码
        Wrapper<UserInfo> queryWrapper= new QueryWrapper<UserInfo>()
                .eq("user_name",username);
        UserInfo userInfo = null;
        try{
            userInfo= userMapper.selectOne(queryWrapper);
        }catchException e){
        	throw new AccountException("系统异常,找到多个用户"+username);
        }
        if(userInfo==null){
            throw new UnknownAccountException(username+"用户不存在");
        }
        log.info(">>>>>>>>验证成功!"+userInfo);
        return new SimpleAuthenticationInfo(userInfo,userInfo.getUserPassword(),ShiroRealm.class.getName());
    }
}

编写Controller层

@RestController
@Slf4j
public class LoginController {
    @PostMapping("/login")
    public String login(String username,String password){

        Subject subject = SecurityUtils.getSubject();
        subject.login(new UsernamePasswordToken(username,password));
        return "success";
    }
    @GetMapping("/pay")
    public String pay(){
        return "success";
    }
}

这样前端代码发送请求即可实现验证

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值