首页IT科技springboot安全管理(2流高手速成记(之五):Springboot整合Shiro实现安全管理)

springboot安全管理(2流高手速成记(之五):Springboot整合Shiro实现安全管理)

时间2025-05-05 01:16:09分类IT科技浏览3828
导读:废话不多说,咱们直接接上回...

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

上一篇我们讲了如何使用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.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
声明:本站所有文章   ,如无特殊说明或标注                 ,均为本站原创发布         。任何个人或组织               ,在未征得本站同意时,禁止复制               、盗用、采集              、发布本站内容到任何网站                 、书籍等各类媒体平台      。如若本站内容侵犯了原著者的合法权益              ,可联系我们进行处理                 。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
windows11发热(Win11发热严重怎么办?Win11中的过热问题解决方法) java注解有哪些(java基础-注解)