springBoot集成shiro进行角色权限认证

13 篇文章 0 订阅
9 篇文章 0 订阅


提示:以下是本篇文章正文内容,下面案例可供参考

一 、shiro简介

1.1 官网和github

shiro官网

shiro github

1.2 概括

Apache Shiro™是一个功能强大且易于使用的Java安全框架,用于执行身份验证,授权,加密和会话管理。
使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。

1.3 核心组件介绍

1、UsernamePasswordToken
	Shiro 用来封装用户登录信息,使用用户的登录信息创建令牌 Token,
	登录的过程即 Shiro 验证令牌是否具有合法身份以及相关权限。
2、 SecurityManager
	Shiro 的核心部分,负责安全认证与授权。
3、Subject
	Shiro 的一个抽象概念,包含了用户信息。
4、Realm
	开发者自定义的模块,根据项目的需求,验证和授权的逻辑在 Realm 中实现。
5、AuthenticationInfo
	用户的角色信息集合,认证时使用。(一般在开发者自定义的模块中配置)
6、AuthorizationInfo
	角色的权限信息集合,授权时使用。(一般在开发者自定义的模块中配置)
7、DefaultWebSecurityManager
	安全管理器,开发者自定义的 Realm 需要注入到 DefaultWebSecurityManager 进行管理才能生效。
8、ShiroFilterFactoryBean
	过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,
	具体的执行操作就是由 ShiroFilterFactoryBean 创建一个个 Filter 对象来完成

1.4 Shiro 运行机制

在这里插入图片描述

二 、springBoot整合shiro(密码加盐方式)

2.0 pom.xml

       <!-- Shiro整合Spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0-RC2</version>
        </dependency>

2.1 ShiroConfig.java


import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author: cw
 * @Date: 2021/1/6 13:46
 * @Description:
 */

@Configuration
@Slf4j
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        log.info("ShiroFilterFactoryBean方法开始");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 添加shiro的内置过滤器
        /*
             anon:无需认证就可以访问
             authc:必须认证了才能让问
             user:必循拥有 记住我 功能才能用
             perms:拥有堆某个资源的权限才能访问
             rolle:拥有某个角色权限才能访问
         */
        // 授权,正常的情况下,没有授权会跳转到未授权页面
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
        filterChainDefinitionMap.put("/login/**", "anon");

        filterChainDefinitionMap.put("/admin/**", "authc");
        filterChainDefinitionMap.put("/user/**", "authc");
        // 主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        // 设置登录路由
        // shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置没授权路由
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        log.info("ShiroFilterFactoryBean方法结束");
        return shiroFilterFactoryBean;

    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(customRealm());
        return defaultSecurityManager;
    }

    @Bean
    public MyCustomRealm customRealm() {
        MyCustomRealm myCustomRealm = new MyCustomRealm();
        // 告诉realm,使用credentialsMatcher加密算法类来验证密文
        myCustomRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        myCustomRealm.setCachingEnabled(false);
        return myCustomRealm;
    }


    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * *
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * *
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

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

    // shiro加密配置
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列的次数,比如散列两次,相当于 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        // storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

}

2.1 MyCustomRealm.java

此处账号密码加盐处理,两次hash与 ShiroConfig 的加密配置保持一致

package com.mint.shiro.config;

import com.mint.shiro.model.po.UsersPo;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.util.HashSet;
import java.util.Set;

/**
 * @Author: cw
 * @Date: 2021/1/6 13:50
 * @Description:
 */

@Slf4j
public class MyCustomRealm extends AuthorizingRealm {


    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        log.info("授权方法开始:[current-username-info:{}]", SecurityUtils.getSubject().getPrincipal());
        // 获取当前登录的这个对象,此处与认证时候 SimpleAuthenticationInfo 的第一个参数对象对应
        UsersPo usersPo = (UsersPo) SecurityUtils.getSubject().getPrincipal();
        // 授权 新建一个授权模块 SimpleAuthorizationInfo 把 权限赋值给当前的用户
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        // 设置当前会话拥有的角色 实际场景根据业务来如从数据库获取角色列表
        Set<String> roles=new HashSet<>();
        roles.add("admin");
        roles.add("finance");
        info.setRoles(roles);

        //设置当前会话可以拥有的权限 实际场景根据业务来如从数据库获取角色列表下的权限列表
        Set<String> permissions=new HashSet<>();
        permissions.add("app:setting:setting");
        permissions.add("app:article:article");
        info.setStringPermissions(permissions);
        log.info("授权方法结束:[current-username-info:{}]", SecurityUtils.getSubject().getPrincipal());

        return info;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        String username = (String) authenticationToken.getPrincipal();
        String password = new String((char[]) authenticationToken.getCredentials());
        log.info("认证方法开始:[current-username:{},current-password:{},password:{}]",authenticationToken.getPrincipal(),authenticationToken.getCredentials(),password);
        // 根据用户名从数据库获取密码
//        UsersPo userNamePassword = userService.selectUserNamePassword(userName);
//        String password = userNamePassword.getPassword();
//        String salt = userNamePassword.getSalt();
        //对身份+证明的数据认证 这里模拟了一个数据源
        //如果是数据库 那么这里应该调用数据库判断用户名密码是否正确
        if (!"admin".equals(username)|| !"123456".equals(password)) {
            throw new IncorrectCredentialsException("账号或密码不正确");
        }
        // 模拟从数据获取密码 MD5加密密码 加盐值为"abc", hash两次,与shiro加密配置保持一致
        String MD5Pass = new SimpleHash("MD5", password, "abc", 2).toString();
        // 认证通过
        UsersPo user = new UsersPo();
        user.setId(1L);//假设用户ID=1
        user.setUsername(username);
        user.setPassword(password);
        user.setSalt("abc");
        // 建立一个 SimpleAuthenticationInfo 认证模块,包括了身份】证明等信息 非加密配置情况下
        // SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password,getName());
        // 建立一个 SimpleAuthenticationInfo 认证模块,包括了身份】证明等信息 加密配置情况下
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, MD5Pass,getName());
        // 密码加盐处理,得到结果会与 从库中拿到的 MD5Pass 进行比较 此处固定 盐值为 "abc"
        info.setCredentialsSalt(ByteSource.Util.bytes("abc"));
        log.info("认证方法结束");
        return info;

    }
}


2.3 IndexController.java


import com.mint.starter.common.ResultCode;
import com.mint.starter.common.ResultUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: cw
 * @Date: 2021/1/6 14:36
 * @Description:
 */

@Controller
@Api(tags = "shiro操作")
@Slf4j
public class IndexController {


    // shiro 认证成功后默认跳转页面
    @GetMapping("/index")
    public String index() {
        return "index";
    }

    @GetMapping("/403")
    public String err403() {
        return "403";
    }

    @GetMapping("/article")
    @RequiresPermissions("app:article:article")
    public String article() {
        return "article";
    }

    @GetMapping("/setting")
    @RequiresPermissions("app:setting:setting")
    public String setting() {
        return "setting";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @GetMapping("/login.jsp")
    public String loginJsp() {
        return "login";
    }

    @PostMapping("/login")
    @ResponseBody
    public Object loginsubmit(String username, String password) {
        Map<String, Object> map = new HashMap<>();
        log.info("登录请求入口:[username:{},password:{}]", username, password);
        //把身份 useName 和 证明 password 封装成对象 UsernamePasswordToken
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //获取当前的 subject
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            return ResultUtil.SUCCESS();
        } catch (IncorrectCredentialsException e) {
            log.error("账号或者密码输入有误~~");
            e.printStackTrace();
            return ResultUtil.ERROR(ResultCode.ACCOUNT_NOT_CORRECT);
        } catch (AuthenticationException e) {
            log.error("账号或者密码输入错误~~");
            e.printStackTrace();
            return ResultUtil.ERROR(ResultCode.ACCOUNT_NOT_CORRECT);
        }
    }
}

三、验证测试

3.1 展示一

项目启动所有路径请求都被拦截到下面登录页面
在这里插入图片描述
密码错误情况展示:
在这里插入图片描述
在这里插入图片描述

3.2 展示二

账号成功情况展示:(认证)
在这里插入图片描述
在这里插入图片描述

3.3 展示三

访问链接情况展示:(授权成功)
在这里插入图片描述
在这里插入图片描述
访问链接情况展示:(授权失败)
修改其固定权限列表信息后重启应用,配置全局异常处理 可参考另一篇文章
在这里插入图片描述
在这里插入图片描述

项目shiro模块:github地址

参考文章:

https://cloud.tencent.com/developer/article/1643122

https://blog.csdn.net/Lucky_Boy_Luck/article/details/106784840

https://blog.csdn.net/bandaoyu/article/details/104849822

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
课程简介:历经半个多月的时间,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也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值