废话不多说,咱们直接接上回

上一篇我们讲了如何使用Springboot框架整合Nosql,并于文章最后部分引入了服务端Session的概念

而早在上上一篇中,我们则已经讲到了如何使用Springboot框架整合Mybatis/MybatisPlus实现业务数据的持久化(写入数据库)

本篇我们把关注点放在一个于这两部分有共同交集的内容——安全管理,并且引入我们今天的主角——Shiro框架

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

—— 来自百度百科

Shiro框架包含三个核心组件:

Subject —— 泛指当前与Shiro交互中的实体,可以是用户或者某后台进程

SecurityManager —— Shiro的核心组件,对内管理各种组件实例,对外提供各种安全服务

Realm —— Shiro与安全数据之间的桥接器

Shiro框架还包含有其他诸多概念,为降低大家的心智负担,这些我们暂且不谈,文末会给大家推荐延展阅读的相关文章

还是老规矩直接上干货,以完整的实例让大家对【如何基于Shiro实现权限的细粒度控制】有一个整体上的认知

 

不知道大家会不会觉得项目结构突然变复杂?别担心,接下来我会给大家逐一拆解

1. 创建数据表

首先是角色表——role

然后是用户表——user

最后是权限表——permission

2. 创建三个对应的Mapper

package com.example.hellospringboot.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.hellospringboot.model.Role;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository; @Mapper
@Repository
public interface RoleMapper extends BaseMapper<Role> {
}
package com.example.hellospringboot.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.hellospringboot.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository; @Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
}
package com.example.hellospringboot.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.hellospringboot.model.Permission;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository; @Mapper
@Repository
public interface PermissionMapper extends BaseMapper<Permission> {
}

这里我们用到了上上一节讲到的内容

这里的Mapper会辅助于后续的安全数据读取

3. 接下来是Service及其实现类

package com.example.hellospringboot.service;

import com.example.hellospringboot.model.Role;

public interface RoleService {
Role findRoleById(int id);
}
package com.example.hellospringboot.service.impl;

import com.example.hellospringboot.mapper.RoleMapper;
import com.example.hellospringboot.model.Role;
import com.example.hellospringboot.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class RoleServiceImpl implements RoleService { @Autowired
RoleMapper mapper; public Role findRoleById(int id){
Role role = mapper.selectById(id);
return role;
}
}
package com.example.hellospringboot.service;

import com.example.hellospringboot.model.User;

public interface UserService {
boolean checkUserByUsernameAndPassword(String userName, String passWord);
User findUserByUserName(String userName);
}
package com.example.hellospringboot.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.hellospringboot.mapper.UserMapper;
import com.example.hellospringboot.model.User;
import com.example.hellospringboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.util.List; @Service
public class UserServiceImpl implements UserService { @Autowired
UserMapper mapper; public boolean checkUserByUsernameAndPassword(String userName, String passWord){
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper = wrapper.eq("user_name", userName).eq("pass_word",passWord);
List<User> userList = mapper.selectList(wrapper);
return userList.size() > 0;
} public User findUserByUserName(String userName){
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper = wrapper.eq("user_name", userName);
User user = mapper.selectOne(wrapper);
return user;
} }
package com.example.hellospringboot.service;

import com.example.hellospringboot.model.Permission;

import java.util.List;

public interface PermissionService {
List<Permission> findPermissionsByRoleId(int roleId);
}
package com.example.hellospringboot.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.hellospringboot.mapper.PermissionMapper;
import com.example.hellospringboot.model.Permission;
import com.example.hellospringboot.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.util.List; @Service
public class PermissionServiceImpl implements PermissionService { @Autowired
PermissionMapper mapper; public List<Permission> findPermissionsByRoleId(int roleId){
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
wrapper = wrapper.eq("role_id", roleId);
List<Permission> list = mapper.selectList(wrapper);
return list;
} }

ok,我们已经准备好了所有的安全数据,及对应的读取方法

到这里,我们就算是做好了所有的准备工作

接下来看我们如何通过Shiro框架来运用这些已经装配好的枪炮子弹

4. 引入Shiro框架相关依赖(pom.xml)

        <!-- 引入shiro框架依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.10.0</version>
</dependency>

这次pom.xml终于不是第一步了,哈哈哈。。。

5. 创建Realm嫁接Shiro框架及安全数据(realm/MyAuthorizingRealm)

package com.example.hellospringboot.realm;

import com.example.hellospringboot.model.Permission;
import com.example.hellospringboot.model.Role;
import com.example.hellospringboot.model.User;
import com.example.hellospringboot.service.PermissionService;
import com.example.hellospringboot.service.RoleService;
import com.example.hellospringboot.service.UserService;
import org.apache.shiro.authc.*;
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.springframework.beans.factory.annotation.Autowired; import java.util.HashSet;
import java.util.List;
import java.util.Set; public class MyAuthorizingRealm extends AuthorizingRealm { @Autowired
UserService userService; @Autowired
RoleService roleService; @Autowired
PermissionService permissionService; @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String userName = token.getUsername();
String passWord = String.valueOf(token.getPassword());
if (!userService.checkUserByUsernameAndPassword(userName, passWord)) {//判断用户账号是否正确
throw new UnknownAccountException("用户名或密码错误!");
}
return new SimpleAuthenticationInfo(userName, passWord, getName());
} @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
String userName = principalCollection.getPrimaryPrincipal().toString();
User user = userService.findUserByUserName(userName);
if (user == null) {
throw new UnknownAccountException("用户名或密码错误!");
}
List<Integer> rolesList = user.rolesList();
Set<String> roles = new HashSet<>();
Set<String> permissions = new HashSet<>();
for (Integer roleId : rolesList) {
Role role = roleService.findRoleById(roleId);
roles.add(role.getName());
List<Permission> permissionList = permissionService.findPermissionsByRoleId(roleId);
for (Permission permission : permissionList) {
permissions.add(permission.getName());
}
}
info.setRoles(roles);
info.setStringPermissions(permissions);
return info;
}
}

Realm的创建对于整个Shiro安全验证体系搭建而言是至关重要的一步!

其中两个抽象方法

doGetAuthenticationInfo —— 用于校验用户名及密码的合法性

doGetAuthorizationInfo —— 用于赋予实体对应的角色及交互权限

6. 测试用Controller创建

package com.example.hellospringboot.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RequestMapping("/user")
@RestController
public class UserController { @PostMapping("/login")
public String login(String user, String pass) {
UsernamePasswordToken token = new UsernamePasswordToken(user, pass);
Subject subject = SecurityUtils.getSubject();
if(!subject.isAuthenticated()) {
try {
subject.login(token);
} catch (AuthenticationException e) {
return e.getMessage();
}
}
return "ok";
} @PostMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated()) {
try {
subject.logout();
} catch (AuthenticationException e) {
return e.getMessage();
}
}
return "ok";
} @GetMapping("/admin")
public String admin() {
return "admin";
} @GetMapping("/user")
public String user() {
return "user";
} }

内容很简单:

login——登录方法

logout——登出方法

admin、user——两个测试方法,用于测试不同角色对于不同方法可访问的细粒度控制

7. ShiroConfig配置类创建,实现用户访问权限的细粒度控制

package com.example.hellospringboot.configure;

import com.example.hellospringboot.realm.MyAuthorizingRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap;
import java.util.Map; @Configuration
public class ShiroConfig { @Bean
public SecurityManager securityManager(Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
} @Bean
public MyAuthorizingRealm getRealm() {
MyAuthorizingRealm realm = new MyAuthorizingRealm();
return realm;
} @Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
Map<String, String> filterChainMap = new LinkedHashMap<String, String>();
filterChainMap.put("/user/login", "anon");
filterChainMap.put("/user/logout", "anon");
filterChainMap.put("/user/admin", "authc,roles[admin],perms[admin:read]");
filterChainMap.put("/user/user", "authc,roles[user],perms[user:read]");
shiroFilter.setFilterChainDefinitionMap(filterChainMap);
return shiroFilter;
}
}

securityManager 和 getRealm 显示指定了Shiro两大组件的实例声明

shiroFilterFactoryBean 则是实现角色访问权限控制的重要方法

filterChainMap.put("/user/login", "anon"); // 代表login方法可以匿名访问

filterChainMap.put("/user/logout", "anon"); // 代表logout方法可以匿名访问

filterChainMap.put("/user/admin", "authc,roles[admin],perms[admin:read]"); // 代表admin方法需要用户满足admin角色,同时具备admin:read权限

filterChainMap.put("/user/user", "authc,roles[user],perms[user:read]"); // 代表user方法需要用户满足user角色,同时具备user:read权限

至此,整个接入流程便结束了

我们再次结合最开始我们配置的数据来对业务逻辑进行分析

用户 admin,同时具备admin、user两种角色

用户 juste,仅具备user一种角色

角色 admin,同时具备admin:write、admin:read两种权限

角色 user,同时具备user:write、user:read两种权限

因此

用户 admin,同时具备admin:write、admin:read、user:write、user:read 四种操作权限

用户 juste,同时具备user:write、user:read两种操作权限

大家理清楚这其中的关系了吗?^ ^

8. 执行Postman验证结果

我们在执行login之前,admin方法无权访问

登录admin之后,同时具备admin和user方法的访问权限

logout登出,然后login登录普通用户juste

会发现依然具备user方法的访问权限,但是失去了admin方法的访问权限

到此,验证我们基于Shiro框架的细粒度权限控制已经实现

除了Shiro框架,我们还有另一个选择,那就是同样可以通过集成Spring Security框架来达成相同的目的

关于更多Shiro框架的内容,及其和Spring Security之间的异同,大家感兴趣可以参考这篇文章:

Shiro最全基础教程_思月行云的博客-CSDN博客

对于Spring Security框架,我们暂且留个悬念,以后会专门再给大家讲解这部分内容

下一节,我们将把关注点投向微服务领域,SpringCloudAlibaba将会是接下来几个章节的重头戏,敬请期待~

MyAuthorizingRealm

2流高手速成记(之五):Springboot整合Shiro实现安全管理的更多相关文章

  1. 2流高手速成记(之六):从SpringBoot到SpringCloudAlibaba

    咱们接上回 2流高手速成记(之五):Springboot整合Shiro实现安全管理 - 14号程序员 - 博客园 (cnblogs.com) 身边常有朋友说:小项目用PHP.大项目用Java(这里绝无 ...

  2. 2流高手速成记(之七):基于Dubbo&Nacos的微服务简要实现

    本节内容会用到之前给大家讲过的这两篇: 2流高手速成记(之六):从SpringBoot到SpringCloudAlibaba 2流高手速成记(之三):SpringBoot整合mybatis/mybat ...

  3. 2流高手速成记(之三):SpringBoot整合mybatis/mybatis-plus实现数据持久化

    接上回 上一篇我们简单介绍了基于SpringBoot实现简单的Web开发,本节来看Web开发中必不可少的内容--数据持久化 先看项目结构: 1. 创建数据表 打开mysql,打开数据库 test (没 ...

  4. 2流高手速成记(之四):SpringBoot整合redis及mongodb

    最近很忙,好不容易才抽出了时间,咱们接上回 上次我们主要讲了如何通过SpringBoot快速集成mybatis/mybatis-plus,以实现业务交互中的数据持久化,而这一切都是基于关系型数据库(S ...

  5. 2流高手速成记(之八):基于Sentinel实现微服务体系下的限流与熔断

    我们接上回 上一篇中,我们进行了简要的微服务实现,也体会到了SpringCloudAlibaba的强大和神奇之处 我们仅改动了两个注释,其他全篇代码不变,原来的独立服务就被我们分为了provider和 ...

  6. SpringBoot系列十二:SpringBoot整合 Shiro

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合 Shiro 2.具体内容 Shiro 是现在最为流行的权限认证开发框架,与它起名的只有最初 ...

  7. SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理|前后端分离(下)----筑基后期

    写在前面 在上一篇文章<SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期>当中,我们初步实现了SpringBoot整合Shiro ...

  8. 补习系列(6)- springboot 整合 shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

  9. SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

    SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...

随机推荐

  1. HCIA-Datacom 3.2 实验二:生成树基础实验

    实验介绍 以太网交换网络中为了进行链路备份,提高网络可靠性,通常会使用冗余链路.但是使用冗余链路会在交换网络上产生环路,引发广播风暴以及MAC地址表不稳定等故障现象,从而导致用户通信质量较差,甚至通信 ...

  2. 万物皆可集成系列:低代码释放用友U8+深度价值(2)—数据拓展应用

    在上一篇内容我们介绍了如何利用低代码开发套件实现低代码应用与U8+系统的对接集成,本次给大家带来的是如何将用友U8+系统中的数据进行价值扩展和实际应用. 我们以生产物料齐套分析为例来说明如何利用低代码 ...

  3. 【Java】学习路径46-两种创建多线程的方法、以及在匿名内部类创建线程

    两种方法: 1.创建一个继承自Thread的线程类,然后再main(不限)中构造这个线程类对象.方法在之前讲过. 2.创建一个使用Runnable接口的线程类,然后在main(不限)中构造这个Runn ...

  4. 如何在Apple Silicon Mac上主动安装Rosetta2

    前提是您的电脑搭载了Apple Silicon处理器 command + space(空格),输入"终端",打开. 输入 : softwareupdate --install-ro ...

  5. metasploit进行局域网远控

    用metasploit进行局域网远程控制 Metasploit是一款开源的安全漏洞检测工具,可以帮助安全和IT专业人士识别安全性问题,验证漏洞的缓解措施,并管理专家驱动的安全性进行评估,提供真正的安全 ...

  6. Linux虚拟机破解密码步骤

    Linux破解密码 重启系统 到达logo界面快速 按 e 编辑当前条目 将光标移至以 linux 开头的行,此为内核命令行 在UTF-8(RHEL7):ro(RHEL8)后添加 rd.break ( ...

  7. KingbaseES R3集群备库执行sys_backup.sh物理备份案例

    案例说明: KingbaseES R3的后期版本支持通过sys_backup.sh执行sys_rman的物理备份,实际上是调用了sys_rman_v6的工具做物理备份.本案例是在备库上执行集群的备份, ...

  8. Spark 读Hive并写入HBase

    package com.grady import org.apache.hadoop.hbase.HBaseConfiguration import org.apache.hadoop.hbase.c ...

  9. (数据科学学习手札141)利用Learn Git Branching轻松学习git常用操作

    1 简介 大家好我是费老师,Git作为世界上最流行的版本控制系统,可以说是每一位与程序打交道的朋友最值得学习的软件之一.除了管理自己的项目,如果你对参与开源项目感兴趣,那么Git更是联结Github. ...

  10. 萌新码农的第一篇:MarkDown的使用方法

    MarkDown的使用方法 使用的编辑软件Typora 1.标题的使用方法 输入''#''然后空格,输入标题名字即可生成标题. 随着''#''的增多,标题的大小会依次减小,最多到六级标题 2.排字 字 ...