SpringBoot中Shiro权限管理的整合及使用--附一个简单的项目

说明:本文主要配合一个简单的项目来讲解shiro的使用,文末会附上项目的github地址供下载参考。

shiro的简介

shiro是一个轻量级的安全框架,包含用户认证和用户授权

shiro的特性:

Authentication(认证):用户身份识别,通常被称为用户“登录”
Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
以上四点,被 Shiro 框架的开发团队称之为应用安全的四大基石。

shiro架构的核心API:

Subject:用户主体(把操作交给SecurityManager)
SecurityManager:安全管理器(管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成保护伞。)
Reaml:shiro连接数据的桥梁,Realm 本质上是一个特定的安全 DAO,它封装与数据源连接的细节,得到Shiro 所需的相关的数据。就是去操作数据库,查询登录账户、密码、权限等

Shiro的配置类:

创建ShiroFilterFactoryBean;(shiro的拦截器,配置哪些路径需要被拦截,哪些不需要被拦截)
创建DefaultWebSecurityManager;(用于设置realm)
创建Reaml(继承AuthorizingReaml);

Shiro内置过滤器,可以实现权限相关的拦截器:

常用的过滤器:
anno:无需认证(登陆)可以访问
authc:必须认证才能访问
user:如果使用rememberMe的功能可以直接访问
perms:该资源必须得到资源权限可以访问
role:该资源必须得到角色权限才能访问

shiro的使用:

在使用Shiro的时候,我们先要确定一下我们的步骤:
1.加入Shiro的依赖包
2.实现自己的Realm类(通过继承AuthorizingRealm类);
3.实现Shiro的配置类
4.实现前端的登录界面以及编写Controller类

第一步:在pom.xml中加入依赖包

 <!--添加shiro支持-->
   <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.4.0</version>
   </dependency>
   <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.0</version>
   </dependency>

第二步:创建自定义MyRealm类

我们需要创建自定义的MyRealm类继承AuthorizingRealm重写doGetAuthenticationInfo和 doGetAuthorizationInfo方法。其中 doGetAuthenticationInfo 是用来验证用户身份,doGetAuthorizationInfo 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

/**
 * 自定义Realm
 *
 */
public class MyRealm extends AuthorizingRealm {
	@Autowired
	private UserService userService;
	@Autowired
	private RoleMenuMapper roleMenuMapper;
	@Autowired
	private MenuMapper menuMapper;
	@Autowired
	private RoleMapper roleMapper;

	/**
	 * 授权--验证url
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//获取当前登录的用户名
		String name = (String) SecurityUtils.getSubject().getPrincipal();
		// 有了用户就可以拿到对应的角色,有角色就可以拿到对应的菜单
		User user = userService.findByName(name);
		// 这个角色对应 的菜单,先用roleId查找角色和菜单的关联表,再用关联表中的menuId查找对应的menu对象,
		// 再.getPermissions找出权限名
		List<RoleMenu> roleMenuList = roleMenuMapper.findByRoleId(user.getRoleId());//查出所有角色关联的菜单
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		for (RoleMenu roleMenu : roleMenuList) {
			info.addStringPermission(menuMapper.findById(roleMenu.getMenuId()).getPermissions());// 添加权限
		}
		// 设置角色
		Set<String> roles = new HashSet<String>();
		roles.add(roleMapper.findById(user.getRoleId()).getName());
		info.setRoles(roles);
		return info;
	}
	/**
	 * 权限认证--登录
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String name = (String) token.getPrincipal();// 拿到的是用户名 就是UsernamePasswordTokenr的第一个参数 name
		User user = userService.findByName(name);// 根据name找到数据库中的user实体
		if (user != null) {// 这里的一步主要是来判断密码是否正确
			/**
			 * 对于传的三个参数
			 * Object principal:可以传username或者user对象都可以
			 * Object credentials:用户密码:注意这里是指从数据库中获取的password
			 * String realmName:即当前realm的名称(个人尝试:随便传个字符串就行,但不能为null,具体不太明白)
			 */
			return new SimpleAuthenticationInfo(user.getName(), user.getPwd(), "MyRealm");
		} else {
			return null;
		}
	}
}

第三步: 创建Shiro的配置类:

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //必须设置设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //设置没有登录时跳转的页面
        //如果不设置默认会自动寻找web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
         /**
         * 登录成功后要跳转的链接
         * 一般shiro验证成功后,我们需要执行一些操作,比如:缓存用户信息到session,返回页面一些信息等操作
         * 所以,不需要shiro来跳转
         */
        //shiroFilterFactoryBean.setSuccessUrl("/index");
       
         /**
         * 添加拦截器:
         * 常用内置过滤器
         *  anon:无需认证(登录)用户可以直接访问(就相当于游客)
         *  authc:必须要认证才能访问
         *  user:使用了remenberMe的功能的用户可以直接无需登录访问(相当于记住登录状态)
         *  perms:必须获取资源权限才能访问
         *  role:必须获取角色授权才能访问
         *  logout:用户登出,这里不用设置控制器,退出后直接跳转到/
         */
        Map<String,String> filterMap = new LinkedHashMap<String,String>();
        //配置不会被拦截的链接 顺序判断 anon:所有的url都可以匿名访问
        filterMap.put("/static/**","anon");
        filterMap.put("/user/login","anon");
        //配置退出过滤器,其中具体的实现shiro已经替我们实现了
        filterMap.put("/logout", "logout");
        //authc:所有url需要认证才能访问
        filterMap.put("/admin/**", "authc");
        filterMap.put("/houtai/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 创建 安全管理器 SecurityManager
     * @return
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置realm
        securityManager.setRealm(myRealm());
       return securityManager;
   }
    /**
     * 身份认证realm;(这个需要自己写,账号密码校验,权限等)
     * @return
     */

    @Bean
    public MyRealm myRealm(){
        return new MyRealm();
    }
    
    /**
     * shiro生命周期处理器
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    	return new LifecycleBeanPostProcessor();
    }
    
    /**
     * 开启shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助springAOP扫描使用
     * shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean即可实现此功能
     * @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;
    }
    /**
     * 配置 ShiroDialect 用于thymeleaf 和shiro标签配合使用
     * @return
     */
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
}

第四步: 实现前端的登录界面以及编写Controller类:

因为界面、类什么得项目中都有,注释也比较全面,这里就不在展示了。

项目地址:https://github.com/isczy/BookSystem.git

在这里插入图片描述

shiro的执行流程

1.启动项目时,就会加载ShiroConfig配置类

2.输入用户名密码,点击登录后有 subject.login(token);的操作,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置

3.SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证

4.Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。

5.执行自定义Realm中重写的doGetAuthenticationInfo方法,进行验证用户名和密码。正常执行完返回,就说明验证成功,。否则抛异常:UnknownAccountException:用户名不存在/IncorrectCredentialsException:密码错误

4.访问项目中的地址时,会执行自定义Realm中重写的doGetAuthorizationInfo方法,进行url验证授权,看用户是否有该权限

shiro认证过程图

在这里插入图片描述

shiro授权过程图

在这里插入图片描述
最后文章末附上项目地址:

https://github.com/isczy/BookSystem.git
发布了12 篇原创文章 · 获赞 22 · 访问量 2602

猜你喜欢

转载自blog.csdn.net/weixin_45240169/article/details/104036314