Springboot整合shiro

教程视频:去哔哩哔哩搜索楠哥教你学Java。里面有一篇SpringBoot整合Shiro的视频

这里是我自己整理的一个笔记,除了视频里教的,还有一些扩展功能以及一些解释

目录

1、什么是Shiro?

 2、什么是RBAC?

3、Shiro核心组件

4、Springboot整合Shiro

5、认证和授权规则

6、Shiro整合Thymeleaf

7、Controller示例

8、权限管理标准5张表

9、密码加密、加盐

10、多个Realm

11、记住我


1、什么是Shiro?

官网:Apache Shiro | Simple. Java. Security.

是一款主流的Java安全框架,不依赖任何容器,可以运行在Java SE和Java EE项目中,它的主要作用是对访问系统的用户进行身份认证、授权、会话管理、加密等操作。

Shiro 就是用来解决安全管理的系统化框架。Shiro是基于session的身份认证和访问控制框架。

 2、什么是RBAC?

RBAC是Role-Based Access Control(基于角色的访问控制)的缩写。它是一种广泛应用于安全管理中的访问控制模型。RBAC模型通过授予用户不同的角色,并将角色与权限进行关联,来管理对资源的访问。

RBAC模型中的关键概念包括以下几个部分:

  1. 角色(Role):角色是一组具有相似职责和权限的用户集合。例如,管理员、编辑、访客等都可以是角色。

  2. 权限(Permission):权限是指执行特定操作或访问特定资源的能力。例如,读取、写入、删除等操作可以被视为不同的权限。

  3. 用户(User):用户是系统中的个体,可以被授予一个或多个角色。

  4. 资源(Resource):资源是系统中受到访问控制的对象。可以是文件、数据库记录、API接口等。

3、Shiro核心组件

用户、角色、权限

会给角色赋予权限、给用户赋予角色

1、UsernamePasswordToken,Shiro用来封装用户登录信息,使用用户的登录信息来创建令牌Token。

2、SecurityManager,Shiro的核心部分,负责安全认证和授权。

3、Subject,Shiro的一个抽象概念,包含了用户信息。

4、Realm,开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在Reaim中。

5、AuthenticationInfo,用户的角色信息集合,认证时使用。

6、AuthorzationInfo,角色的权限信息集合,授权时使用。

7、DefaultWebSecurityManager,安全管理器,开发者自定义的Realm需要注入到DefaultWebSecurityManager进行管理才能生效

8、ShiroFilterFactoryBean,过滤器工厂,Shiro的基本运行机制是开发者定制规则,Shiro去运行,具体的执行操作就是由ShiroFilterFactoryBean

Shiro的运行机制如下图所示: 

4、Springboot整合Shiro

SpringBoot集成Shiro官网:Integrating Apache Shiro into Spring-Boot Applications | Apache Shiro

 这里说一下,我从一开始学习就用的Springboot3,而3版本整合shrio出现了Servlet和Shiro不生效等问题。所以还是推荐使用Springboot2的版本去整合Shiro。

不过,后面发现一位大佬把这个问题完美的解决了!!!(膜拜大佬╰(*°▽°*)╯)Java17和springboot3.0使用shiro报ClassNotFoundException_星海蔚蓝的博客-CSDN博客

根据大佬,重新将shiro依赖引入,再去使用Springboot3的版本就没有问题了!!!

即使看懂了上面的概念,你不去看视频教学,下面的代码很难看懂的。所以先去看看教学视频,跟着教学实操。

1、创建Spring Boot应用,集成Shiro及相关组件(这里是我练习模块中的依赖,我直接粘过来的)

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <classifier>jakarta</classifier>
            <version>1.11.0</version>
            <!-- 排除仍使用了javax.servlet的依赖 -->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 引入适配jakarta的依赖包 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <classifier>jakarta</classifier>
            <version>1.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <classifier>jakarta</classifier>
            <version>1.11.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.24</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

2、自定义Shiro过滤器

public class AccountRealm extends AuthorizingRealm {
    @Autowired
    private AccountService accountService;

    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取当前登录的用户信息
        Subject subject = SecurityUtils.getSubject();
        Account account = (Account) subject.getPrincipal();

        //设置角色
        Set<String> roles = new HashSet<>();
        roles.add(account.getRole());
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);

        //设置权限
        info.addStringPermission(account.getPerms());
        return info;
    }

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;  //这行代码将传入的 authenticationToken 转换为 UsernamePasswordToken 对象,以便获取用户名和密码。
        Account account = accountService.findByUsername(token.getUsername());
        if(account != null){
            /**
             * 这是返回身份验证信息的代码。SimpleAuthenticationInfo 是 Shiro 框架中的一个实现类,用于封装用户的身份验证信息
             * @Param1  account 参数表示身份验证的主体对象,可以是任何表示用户身份的实体对象
             * @Param2   表示用户的密码,用于进行密码验证
             * @Param3  返回当前 Realm 的名称,用于标识身份验证信息来源,Realm 可以通过其名称进行唯一标识,以便在 Shiro 配置文件中区分和配置不同的 Realm
             */
            return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
        }
        return null;
    }
}

3、配置类

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        //权限设置
        Map<String,String> map = new HashMap<>();
        map.put("/main","authc");
        map.put("/manage","perms[manage]");
        map.put("/administrator","roles[administrator]");
        //设置登录页面
        factoryBean.setLoginUrl("/login");
        //设置未授权页面
        factoryBean.setUnauthorizedUrl("/unauth");
        factoryBean.setFilterChainDefinitionMap(map);
        return factoryBean;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(AccountRealm accountRealm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(accountRealm);
        return manager;
    }

    @Bean
    public AccountRealm accountRealm(){
        return new AccountRealm();
    }

5、认证和授权规则

认证过滤器

  1. anon:无需认证。

  2. authc:必须认证。

  3. authcBasic:需要通过HTTPBasic认证。

  4. user:不一定通过认证,只要曾经被Shiro记录即可,比如:记住我。

授权过滤器

  1. perms:必须拥有某个权限才能访问。

  2. role:必须拥有某个角色才能访问。

  3. port:请求的端口必须是指定值才可以。

  4. rest:请求必须基于RESTful,POST,PUT,GET,DELETE。

  5. ssl:必须是安全的URL请求,协议HTTP。

6、Shiro整合Thymeleaf

1、pom.xml引入依赖

<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

2、配置类添加ShiroDialect 

@Bean
public ShiroDialect shiroDialect(){
    return new ShiroDialect();
}

3、 配置视图解析器

spring:
	thymeleaf:
  		prefix: classpath:/templates/
  		suffix: .html

4、 html

 xmlns:th="http://www.thymeleaf.org"        //加上这个才可以使用thymeleaf语法

xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"        //加上这个就可以使用shiro表达式了


<link rel="shortcut icon" href="#"/>    //加入这个可以解决控制台报错icon的问题 

7、Controller示例

@Controller
public class AccountController {

    @GetMapping("/{url}")
    public String redirect(@PathVariable("url") String url){
        return url;
    }

    @PostMapping("/login")
    public String login(String username, String password,Model model){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//        token.setRememberMe(rememberMe);
        try {
            subject.login(token);
            Account account = (Account) subject.getPrincipal();
            subject.getSession().setAttribute("account",account);
            return "index";
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            model.addAttribute("msg","用户名错误!");
            return "login";
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            model.addAttribute("msg","密码错误!");
            return "login";
        }
    }

    @GetMapping("/unauth")
    @ResponseBody
    public String unauth(){
        return "未授权,无法访问!";
    }

    @GetMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "login";
    }
}

8、权限管理标准5张表

这个去网上随便搜下,就可以看到一些案例表!

9、密码加密、加盐

1、什么是加盐?

加盐的意思就是加上 安全随机数

2、ShiroConfig

@Bean
public AccountRealm accountRealm(){
    AccountRealm accountRealm = new AccountRealm();
    //设置加密算法
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("md5");
    //设置加密次数
    credentialsMatcher.setHashIterations(1);
    //在身份验证过程中,用户提供的凭证(如密码)需要与存储在系统中的凭证进行匹配,以验证用户的身份。凭证匹配器(Credentials Matcher)是用来执行此匹配过程的组件
    accountRealm.setCredentialsMatcher(credentialsMatcher); //配置身份验证领域(Realm)的凭证匹配器

    return accountRealm;
}

3、 Realm

在认证方法返回对象中加入第三个参数

return new SimpleAuthenticationInfo(account,account.getPassword(), ByteSource.Util.bytes(account.getSalt()),getName());

4、产生盐

//SecureRandomNumberGenerator 是一个类,它是 Apache Shiro 库中用于生成安全的随机数的类。它使用加密强度较高的算法来生成随机字节。.nextBytes().toHex()是一个用于生成随机字节序列并转换为十六进制字符串的代码片段
String Salt = new SecureRandomNumberGenerator().nextBytes().toHex();

5、 加密

加密算法和加密次数必须和配置类中配置的一样!!

//SimpleHash是Apache Shiro库中的一个类,用于计算散列值
SimpleHash simpleHash = new SimpleHash("md5",user.getPassword(),Salt,1);

这里解释一下,它是如何利用盐和密码进行的验证:

将用户注册的密码和产生的盐一起进行加密作为密码保存在数据库中,将盐也保存在一个字段中。在进行登录验证时,根据用户名查到对应的用户,然后将你输入的密码和对应的盐值进行同样的算法加密和加密次数,然后将加密后的密码和查询到的用户的密码进行比对,如若相同则登录通过,反之。 

10、多个Realm

如果有多种认证方式,也就是得写多个自定义Realm过滤器时,Shiro会尝试进行身份验证或授权时,它将按照配置的顺序依次调用每个Realm的认证或授权方法。如果某个Realm无法完成验证或授权操作,Shiro将继续尝试下一个Realm,直到找到一个能够验证或授权成功的Realm,或者所有的Realm都被尝试完毕。

如果所有配置的Realm都无法完成验证或授权,Shiro将判断认证或授权过程失败,表示提供的登录信息有误。

需要注意的是,Shiro的Realm在认证过程中可能会抛出异常,例如身份验证失败、连接数据库失败等。当出现异常时,Shiro将终止当前Realm的验证操作并尝试下一个Realm。

配置案例:

@Configuration
public class ShiroConfig {

    // 配置自定义的Realm
    @Bean
    public Realm realm1() {
        // 创建并配置realm1的实例
        return new Realm1();
    }

    // 配置自定义的Realm
    @Bean
    public Realm realm2() {
        // 创建并配置realm2的实例
        return new Realm2();
    }

    // 配置SecurityManager,并指定使用哪些Realm
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealms(Arrays.asList(realm1(), realm2()));
        return securityManager;
    }

}

11、记住我

当用户勾选了"记住我"功能并且成功登录后,网站会在客户端创建一个持久化的cookie来保存用户的登录凭证。当用户再次访问网站时,浏览器会将该cookie发送给服务器,服务器会解析这个cookie并使用其中的信息重新建立一个会话,从而实现自动登录的功能。

具体来说,服务器会使用cookie中的身份标识信息来查找用户的登录凭证,如果凭证有效且未过期,服务器会创建一个新的会话并将用户标记为已登录状态,然后用户就可以继续访问需要登录访问权限的页面,而无需重新输入用户名和密码进行认证。  

shiroConfig

      map.put("/**","user");	    //这个设置 记住我能够访问




    @Bean
    public DefaultWebSecurityManager securityManager(AccountRealm accountRealm,RememberMeManager rememberMeManager){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(accountRealm);
        manager.setRememberMeManager(rememberMeManager);    //设置RememberMe相关配置
        return manager;
    }

    @Bean
    public RememberMeManager rememberMeManager(){
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        //设置cookie名称,对应login.html页面的<input type="checkbox" name="rememberMe"/>
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //防止某些攻击,不被Javascript代码获取
        simpleCookie.setHttpOnly(true);
        //单位为秒,设置cookie过期时间
        simpleCookie.setMaxAge(7*24*60*60);
        //设置Cookie
        rememberMeManager.setCookie(simpleCookie);
        //设置"Remember Me"功能中使用的密钥的代码
     	rememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        //密钥 完整步骤为:
        // 使用Base64编码字符串解码得到字节数组
		//byte[] cipherKey = Base64.decode("4AvVhmFLUs0KTA3Kprsdag==");	//实际应用中,应该生成一个随机唯一的密钥
		// 将解码后的字节数组设置为RememberMeManager的密钥
		//rememberMeManager.setCipherKey(cipherKey);
        
        return rememberMeManager;
    }

 controller

@PostMapping("/login")
public String login(String username, String password,boolean rememberMe,Model model){
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken(username,password,rememberMe);	
    System.out.println(rememberMe);//rememberMe为true则shiro会帮我们记住用户的登录状态
    try {
        subject.login(token);
        Account account = (Account) subject.getPrincipal();
        subject.getSession().setAttribute("account",account);
        return "index";
    } catch (UnknownAccountException e) {
        e.printStackTrace();
        model.addAttribute("msg","用户名错误!");
        return "login";
    } catch (IncorrectCredentialsException e) {
        e.printStackTrace();
        model.addAttribute("msg","密码错误!");
        return "login";
    }
}

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2j27rgyk150k8

  • 10
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring BootSpring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值