草庐IT

shiro认证- SpringBoot(20)

liwenruo 2023-04-19 原文

1.认识shiro

  除Spring Security安全框架外,应用非常广泛的就是Apache的强大又灵活的开源安全框架 Shiro,在国内使用量远远超过Spring Security。它能够用于身份验证、授权、加密和会话管理, 有易于理解的API,可以快速、轻松地构建任何应用程序。而且大部分人觉得从Shiro入门要比 Spring Security 简单。

  1.1 认识Shiro的核心组件

Shiro有如下核心组件。

  • Subject:代表当前“用户”。与当前应用程序交互的任何东西都是Subject,如爬虫、机器人、所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给 SecurityManager,Subject 是一个门面,SecurityManager 是实际的执行者。
  • SecurityManager:与安全有关的操作都会与SecurityManager交互。它管理着所有 Subject,是Shiro的核心,员责与其他组件进行交互。
  • Realm: Shiro从Realm中获取安全数据(用户、角色、权限),SecurityManager 需要 从Realm中获取相应的用户信息进行比较用户身份是否合法,也需要从Realm中得到用户 相应的角色/权限进行验证,以确定用户是否能进行操作。

2.实例:用shiro实现管理后台的动态权限功能

  依赖:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.9.1</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

  2.1 创建实体

  2.1.1 创建管理员实体

  创建管理实体,用于存放管理员信息,见以下代码:

package com.intehel.demo.domain;

import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

@Entity
@Data
public class Admin implements Serializable {
    @Id
    @GeneratedValue
    private Integer id;
    @Column(unique = true)
    //账号
    private String username;
    //名称
    private String name;
    //密码
    private String password;
    //盐加密
    private String salt;
    //用户状态:0:创建未认证,等待验证 1:正常状态 2:用户被锁定
    private byte state;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "SysUserRole",joinColumns = {@JoinColumn(name = "uid")},
            inverseJoinColumns = {@JoinColumn(name = "roleId")})
    private List<SysRole> rolesList;
}

  2.1.2 创建权限实体

  权限实体用于存放权限数据,见以下代码:

package com.intehel.demo.domain;

import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

@Entity
@Data
public class SysPermission implements Serializable {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    @Column(columnDefinition = "enum('menu','button')")
    private String resourceType;
    private String url;
    private String permission;
    private Long parentId;
    private String parentIds;
    private Boolean avaliable = Boolean.FALSE;
    @ManyToMany
    @JoinTable(name = "SysRolePermission",joinColumns = {@JoinColumn(name = "permissionId")},
            inverseJoinColumns = {@JoinColumn(name = "roleId")})
    private List<SysRole> rolesList;

}

  2.1.3 创建角色实体

  角色实体是管理员的角色,用于对管理员分组,并通过与权限表映射来确定管理员的权限,见以下代码:

package com.intehel.demo.domain;

import lombok.Data;
import javax.persistence.*;
import java.util.List;

@Entity
@Data
public class SysRole {
    @Id
    @GeneratedValue
    private Integer id;
    @Column(unique = true)
    private String role;
    private String description;
    private Boolean available = Boolean.FALSE;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "SysRolePermission",joinColumns = {@JoinColumn(name = "roleId")},
            inverseJoinColumns = {@JoinColumn(name = "permissionId")})
    private List<SysPermission> permissions;
    @ManyToMany
    @JoinTable(name = "SysUserRole",joinColumns = {@JoinColumn(name = "roleId")},
            inverseJoinColumns = {@JoinColumn(name = "uid")})
    private List<Admin> admins;
}

  2.2 进行权限配置

package com.intehel.demo.realm;

import com.intehel.demo.domain.Admin;
import com.intehel.demo.domain.SysPermission;
import com.intehel.demo.domain.SysRole;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class CustomerRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Admin adminInfo = (Admin) principalCollection.getPrimaryPrincipal();
        for (SysRole role : adminInfo.getRolesList()) {
            info.addRole(role.getRole());
            for (SysPermission p:role.getPermissions()){
                info.addStringPermission(p.getPermission());
            }
        }
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username = (String) authenticationToken.getPrincipal();
        System.out.println(authenticationToken.getCredentials());
        Admin adminInfo = new Admin();
        adminInfo.setUsername("long");
        adminInfo.setPassword("longzhonghua");
        adminInfo.setSalt("yan");
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                adminInfo,adminInfo.getPassword(),
                ByteSource.Util.bytes(adminInfo.getSalt()),
                getName()
        );
        return info;
    }
}

  2.3 将shiro注入到spring容器中

package com.intehel.demo.config;

import com.intehel.demo.realm.CustomerRealm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return  defaultAdvisorAutoProxyCreator;
    }

    // 将自己验证的方式加入到容器中
    @Bean
    public CustomerRealm customRealm() {
        CustomerRealm customRealm = new CustomerRealm();
        return customRealm;
    }

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager  defaultSecurityManager = new DefaultWebSecurityManager ();
        defaultSecurityManager.setRealm(customRealm());
        return defaultSecurityManager;
    }
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String,String> map = new HashMap<>();
        // 登出
        map.put("/logout","logout");
        // 对所有用户进行认证
        map.put("/**","authc");
        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 错误页面 认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }


    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


}

  2.4 编写控制层

package com.intehel.demo.controller;
import com.intehel.demo.domain.Admin;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class LoginController {

    @GetMapping("/login")
    public String login(Admin user) {
        if(StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())) {
            return "请输入用户名和密码";
        }

        // 用户认证信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(),user.getPassword());
        try {
            // 进行验证,这里可以捕获异常,然后返回对应信息
            subject.login(usernamePasswordToken);
        }catch (UnknownAccountException e) {
            log.error("用户名不存在",e);
            return "用户名不存在";
        } catch (AuthenticationException e) {
            log.error("账号或者密码错误!",e);
            return "账号或者密码错误!";
        } catch (AuthorizationException e) {
            log.error("没有权限!",e);
            return "没有权限";
        }
        return "login success";
    }


    @RequiresRoles("admin")
    @GetMapping("/admin")
    public String admin() {
        return "admin Success!";
    }

    @RequiresPermissions("query")
    @GetMapping("/index")
    public String index() {
        return "index success";
    }

    @RequiresPermissions("add")
    @GetMapping("/add")
    public String add() {
        return "add success";
    }

}

  2.5 测试权限

  (1)向sql中插入数据

INSERT INTO `admin` (`id`, `name`, `password`, `salt`, `state`, `username`) VALUES (1, '管理员', '32baebda76498588dabf64c6e8984097', 'yan', 0, 'long');
INSERT INTO `sys_permission` (`id`, `avaliable`, `name`, `parent_id`, `parent_ids`, `permission`, `resource_type`, `url`) VALUES (1, b'0', '用户管理', 0, '0/', 'admin:view', 'menu', 'admin/list');
INSERT INTO `sys_permission` (`id`, `avaliable`, `name`, `parent_id`, `parent_ids`, `permission`, `resource_type`, `url`) VALUES (2, b'0', '用户添加', 1, '0/1', 'admin:add', 'button', 'admin/add');
INSERT INTO `sys_permission` (`id`, `avaliable`, `name`, `parent_id`, `parent_ids`, `permission`, `resource_type`, `url`) VALUES (3, b'0', '用户删除', 1, '0/1', 'admin:del', 'button', 'admin/del');
INSERT INTO `sys_role` (`id`, `available`, `description`, `role`) VALUES (1, b'0', '管理员', 'admin');
INSERT INTO `sys_role_permission` (`role_id`, `permission_id`) VALUES (1, 1);
INSERT INTO `sys_role_permission` (`role_id`, `permission_id`) VALUES (1, 2);
INSERT INTO `sys_role_permission` (`role_id`, `permission_id`) VALUES (1, 3);
INSERT INTO `sys_user_role` (`role_id`, `uid`) VALUES (1, 1);

   (2)测试登录

  

  2.6  对比 Spring Security 与 Shiro

  (1)Shiro的特点

  • 功能强大,且简单、灵活。
  • 拥有易于理解的API。
  • 简单的身份认证(登录),支持多种数据源(LDAP、JDBC、Kerberos、ActiveDirectory等)。
  • 支持对角色的简单签权,并且支持细粒度的签权。
  • 支持一级缓存,以提升应用程序的性能。
  • 内置的基于POJO会话管理,适用于Web,以及非Web环境。
  • 不跟任何的框架或容器捆绑,可以独立运行。

  (2)Spring Security 的特点。

  • Shiro的功能它都有
  • 对防止CSRF跨站、XSS跨站脚本可以很好地实现,对Oauth、OpenlD也有支持。Shiro 则需要开发者自己手动实现
  • 因为Spring Security是Spring自己的产品,所以对Spring的支持极好,但也正是因为这个,所以仅仅支持自己的产品,导致其捆绑到了 Spring框架,而不支持其他框架。
  • Spring Security的权限细粒度更高(这不是绝对的,Shiro也可以实现)。

 

有关shiro认证- SpringBoot(20)的更多相关文章

  1. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  2. ruby - token 认证 - 2

    简单代码require'net/http'url=URI.parse('getjson/otherdatahere[link]')req=Net::HTTP::Get.new(url.to_s)res=Net::HTTP.start(url.host,url.port){|http|http.request(req)}putsres.body只是想知道如何在phpcURL中放置身份验证token,我是这样做的    curl_setopt($ch,CURLOPT_HTTPHEADER,array('Authorization:Bearerxxx'));//Bearertokenfora

  3. ruby - HTTParty 摘要认证 - 2

    谁能提供一个使用HTTParty和digestauth的例子?我在网上找不到例子,希望有人能提供一些帮助。谢谢。 最佳答案 您可以在定义类时使用digest_auth方法设置用户名和密码classFooincludeHTTPartydigest_auth'username','password'end 关于ruby-HTTParty摘要认证,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questi

  4. Ubuntu20.04系统WineHQ7.0安装微信 - 2

    提供3种Ubuntu系统安装微信的方法,在Ubuntu20.04上验证都ok。1.WineHQ7.0安装微信:ubuntu20.04安装最新版微信--可以支持微信最新版,但是适配的不是特别好;比如WeChartOCR.exe报错。2.原生微信安装:linux系统下的微信安装(ubuntu20.04)--微信适配的最好,反应最快,但是微信版本只到2.1.1,版本太老,很多功能都没有。3.深度deepin-wine6安装微信:ubuntu20.04+系统deepin-wine6安装新版微信--综合比较好,当前个人使用此种方法1个月,微信版本3.4;没什么大问题,尚可。一、WineHQ7.0安装微信

  5. ruby-on-rails - encode_www_form 将空格转换为 + 而不是 %20 - 2

    我正在尝试从使用RubyonRails的散列创建http参数,我尝试使用URI.encode_www_form(params),但这没有正确生成参数。下面是我的哈希值params['Name'.to_sym]='NiaKun'params['AddressLine1'.to_sym]='AddressOne'params['City'.to_sym]='CityName'这个方法把空格转成+,我要的是把空格转成%20我收到"Name=Nia+Kun&AddressLine1=Address+One&City=City+Name"但我需要将此空格转换为%20

  6. springboot定时任务 - 2

    如果您希望在Spring中启用定时任务功能,则需要在主类上添加 @EnableScheduling 注解。这样Spring才会扫描 @Scheduled 注解并执行定时任务。在大多数情况下,只需要在主类上添加 @EnableScheduling 注解即可,不需要在Service层或其他类中再次添加。以下是一个示例,演示如何在SpringBoot中启用定时任务功能:@SpringBootApplication@EnableSchedulingpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.ru

  7. 基于SpringBoot的线上日志阅读器 - 2

    软件特点部署后能通过浏览器查看线上日志。支持Linux、Windows服务器。采用随机读取的方式,支持大文件的读取。支持实时打印新增的日志(类终端)。支持日志搜索。使用手册基本页面配置路径配置日志所在的目录,配置后按回车键生效,下拉框选择日志名称。选择日志后点击生效,即可加载日志。windows路径E:\java\project\log-view\logslinux路径/usr/local/XX历史模式历史模式下,不会读取新增的日志。针对历史文件可以分页读取,配置分页大小、跳转。历史模式下,支持根据关键词搜索。目前搜索引擎使用的是jdk自带类库,搜索速度相对较低,优点是比较简单。2G日志全文搜

  8. springboot使用validator进行参数校验 - 2

    1.依赖导入org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-validation2.validation常用注解@Null被注释的元素必须为null@NotNull被注释的元素不能为null,可以为空字符串@AssertTrue被注释的元素必须为true@AssertFalse被注释的元素必须为false@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值@D

  9. 华为认证的网络工程师证好考吗,含金量高吗 ? - 2

    华为认证分等级的,相当于初中高三个等级,当然高级是比较难考的,也是含金量最高的。我就慢慢给你介绍一下。1.了解华为认证华为认证网络工程师是由华为公司认证与采购部推出的独立认证体系,与之前的华为认证不同,简称HCIA。同时华为认证是华为技术有限公司凭借多年信息通信技术人才培养经验,以及对行业发展的理解,以层次化的职业技术认证为指引,推出的覆盖IP、IT、CT以及ICT融合技术领域的认证体系,是ICT全技术领域认证体系。​2.怎么考取华为认证网络工程师?要考取华为认证网络工程师必须选择最近的Prometric授权考试中心APTC报名并参加GB0-190的考试,考试通过后,以获得由华为统一签发的“华

  10. 停车系统源码-基于springboot+uniapp开源项目 - 2

    Iparking停车收费管理系统-可商用介绍Iparking是一款基于springBoot的停车收费管理系统,支持封闭车场和路边车场,支持微信支付宝多种支付渠道,支持多种硬件,涵盖了停车场管理系统的所有基础功能。技术栈Springboot,MybatisPlus,Beetl,Mysql,Redis,RabbitMQ,UniApp功能云端功能序号模块功能描述1系统管理菜单管理配置系统菜单2系统管理组织管理管理组织机构3系统管理角色管理配置系统角色,包含数据权限和功能权限配置4系统管理用户管理管理后台用户5系统管理租户管理多租户管理6系统管理公众号配置租户公众号配置7系统管理操作日志审计日志8系统

随机推荐