Shiro学习笔记之Shiro与Spring Boot的结合(两种方式)

Shiro学习笔记之Shiro与Spring Boot的结合(两种方式)

前言

本文是在b站学习Shiro时随笔记下,这个视频相比于网上一些文字教程还是比较详细,算是保姆级的教程。虽然时长较长,但对于初学还是比较推荐。
视频地址:https://www.bilibili.com/video/BV1pa4y1471s
本文旨在记录自己的学习过程,也是我学习笔记的第一篇记录,受我目前师父启发后发了这篇文章。我只是java小白一个,目前在实习,如有错误,欢迎指正。

一、JdbcRealm

1.MyBatis导入

<dependencies>     <!--druid starter-->     <dependency>         <groupId>com.alibaba</groupId>         <artifactId>druid-spring-boot-starter</artifactId>         <version>1.1.20</version>     </dependency>     <dependency>         <groupId>mysql</groupId>         <artifactId>mysql-connector-java</artifactId>         <version>5.1.47</version>     </dependency>     <dependency>         <groupId>org.mybatis.spring.boot</groupId>         <artifactId>mybatis-spring-boot-starter</artifactId>         <version>2.1.4</version>     </dependency> </dependencies> 

2.ShiroConfig

@Configuration public class ShiroConfig {      @Bean     public JdbcRealm getJdbcRealm(DataSource dataSource) {         JdbcRealm jdbcRealm = new JdbcRealm();         //JdbcRealm会自行去数据区查询用户及权限数据(数据库的表结构要符合JdbcRealm的规范)         jdbcRealm.setDataSource(dataSource);         //JdbcRealm默认开启认证功能,需要手动开启授权功能         jdbcRealm.setPermissionsLookupEnabled(true);         return jdbcRealm;     }      @Bean     public DefaultWebSecurityManager getDefaultWebSecurityManager(JdbcRealm jdbcRealm) {         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();         //securityManager要完成校验需要realm的支持         securityManager.setRealm(jdbcRealm);         return securityManager;     }      @Bean     public ShiroFilterFactoryBean filter(SecurityManager securityManager) {         ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();         filterFactoryBean.setSecurityManager(securityManager);         //设置shiro的拦截规则         /*             anon    匿名用户可访问             authc   认证用户可访问             user    使用remenberme的用户可访问             perms   对应权限可访问             role    对应角色可访问          */         Map<String, String> filterMap = new HashMap<>();         filterMap.put("/","anon");         filterMap.put("/login.html","anon");         filterMap.put("/index.html","anon");         filterMap.put("/regist.html","anon");         filterMap.put("/user/login","anon");         filterMap.put("/user/regist","anon");         filterMap.put("/static/**","anon");         filterMap.put("/**","authc");          filterFactoryBean.setFilterChainDefinitionMap(filterMap);         filterFactoryBean.setLoginUrl("/login.html");         //设置未授权访问的页面路径         filterFactoryBean.setUnauthorizedUrl("/login.html");         return filterFactoryBean;     } }  

3.前端页面标签使用

JSP页面中引用:

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> 

Thymeleaf模板中引用

(1)在pom.xml文件中导入thymeleaf模板对Shiro的标签支持的依赖
 <!--thymeleaf对shiro的支持--> <dependency>     <groupId>com.github.theborakompanioni</groupId>     <artifactId>thymeleaf-extras-shiro</artifactId>     <version>2.0.0</version> </dependency> 
(2)在ShiroConfig中配置Shiro的方言支持
public class ShiroConfig(){      	@Bean     public ShiroDialect getShiroDialect() {         return new ShiroDialect();     }          //.... }  
(3)Thymeleaf模板中引入Shiro的命名空间
<html lang="en"        xmlns:th="http://www.thymeleaf.org"       xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> </html> 

常用的标签

1.shiro:guest

判断用户是否是游客身份,如果是游客身份则显示此标签内容

<shiro:guest>     欢迎游客登录访问,<a href="login.html">登录</a> </shiro:guest> 

2.shiro:user

判断用户是否是认证身份,如果是认证身份则显示此标签内容

与guest标签效果相反

<shiro:user>     已登录用户! </shiro:user> 

3.shiro:principal

取用户的登录<shiro:user> 欢迎<shiro:principal/>! </shiro:user>

Shiro学习笔记之Shiro与Spring Boot的结合(两种方式)
4.shiro:notAuthenticated/shiro:authenticated

与1、2标签的效果相同,认证过程不同

5.shiro:hasRole

判断当前登录的用户是否有指定的角色,有则显示指定内容

<shiro:user>     欢迎【<shiro:principal/>】!     当前用户为     <shiro:hasRole name="admin">超级管理员</shiro:hasRole>     <shiro:hasRole name="cmanager">仓管人员</shiro:hasRole>     <shiro:hasRole name="smanager">销售人员</shiro:hasRole>     <shiro:hasRole name="kmanager">客服人员</shiro:hasRole>     <shiro:hasRole name="zmanager">行政人员</shiro:hasRole> </shiro:user> 

效果图:
Shiro学习笔记之Shiro与Spring Boot的结合(两种方式)

6.shiro:hasPermission

判断当前登录的用户是否有指定的权限,有则显示指定内容

仓库管理 <ul>     <shiro:hasPermission name="sys:c:save"><li><a href="#">入库</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:c:delete"><li><a href="#">出库</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:c:update"><li><a href="#">修改</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:c:find"><li><a href="#">查询</a></li></shiro:hasPermission> </ul>  订单管理 <ul>     <shiro:hasPermission name="sys:s:save"><li><a href="#">添加订单</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:s:delete"><li><a href="#">删除订单</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:s:update"><li><a href="#">修改订单</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:s:find"><li><a href="#">查询订单</a></li></shiro:hasPermission> </ul>  客户管理 <ul>     <shiro:hasPermission name="sys:k:save"><li><a href="#">添加客户</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:k:delete"><li><a href="#">删除客户</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:k:update"><li><a href="#">修改客户</a></li></shiro:hasPermission>     <shiro:hasPermission name="sys:k:find"><li><a href="#">查询客户</a></li></shiro:hasPermission> </ul> 

效果图:
Shiro学习笔记之Shiro与Spring Boot的结合(两种方式)

二、自定义Realm

1.MyBatis导入

<dependencies>     <!--druid starter-->     <dependency>         <groupId>com.alibaba</groupId>         <artifactId>druid-spring-boot-starter</artifactId>         <version>1.1.20</version>     </dependency>     <dependency>         <groupId>mysql</groupId>         <artifactId>mysql-connector-java</artifactId>         <version>5.1.47</version>     </dependency>     <dependency>         <groupId>org.mybatis.spring.boot</groupId>         <artifactId>mybatis-spring-boot-starter</artifactId>         <version>2.1.4</version>     </dependency> </dependencies> 

2.yml文件配置

spring:   datasource:     druid:       url: jdbc:mysql://localhost:3306/shirostudy       driver-class-name: com.mysql.jdbc.Driver       username: root       password: root       initial-size: 1       min-idle: 1       max-active: 20 mybatis:   mapper-locations: classpath:mappers/*Mapper.xml   type-aliases-package: com.wxy.shiro4.beans 

3.数据库的创建

直接创建就可,无需格式,推荐五张表起步。用户表,角色表,用户-角色表,权限表,角色-权限表。

user

CREATE TABLE `user` (   `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户id',   `user_name` varchar(60) CHARACTER SET utf8 NOT NULL COMMENT '用户名',   `user_pwd` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '用户密码',   `pwd_salt` varchar(30) CHARACTER SET utf8 DEFAULT NULL COMMENT '加盐',   PRIMARY KEY (`user_id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1 COMMENT='用户表'; 

role

CREATE TABLE `role` (   `role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色id',   `role_name` varchar(60) CHARACTER SET utf8 NOT NULL COMMENT '角色名',   `role_desc` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '角色描述',   PRIMARY KEY (`role_id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 COMMENT='角色表'; 

user_role

CREATE TABLE `user_role` (   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,   `user_id` bigint(20) NOT NULL COMMENT '用户id',   `role_id` bigint(20) NOT NULL COMMENT '角色id',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1 COMMENT='用户角色表';	 

permission

CREATE TABLE `permission` (   `per_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '权限id',   `permission` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '权限名称',   `per_desc` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '权限描述',   PRIMARY KEY (`per_id`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1 COMMENT='权限表'; 

role_per

CREATE TABLE `role_per` (   `id` bigint(20) NOT NULL AUTO_INCREMENT,   `role_id` bigint(20) NOT NULL COMMENT '角色id',   `per_id` bigint(20) NOT NULL COMMENT '权限id',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=latin1 COMMENT='角色权限表'; 

4.Dao实现

  • Shiro进行需要用户信息
    • 根据用户名查询用户信息
  • Shiro进行授权管理需要当前用户的角色和权限
    • 根据用户名查询当前用户的角色列表
    • 根据用户名查询当前用户的权限列表

以上三个查询可以直接连接查询(教程也是连接查询)

但我建议采用分开查询,在代码逻辑中进行连接查询可以提高效率。

这里教学中查询结果为单列角色名与权限名,但是我直接查询出List然后循环使用.get()方法。

以下为项目结构:
Shiro学习笔记之Shiro与Spring Boot的结合(两种方式)

UserDao:

public interface UserDao {      public User queryUserByUserName(String userName) throws Exception;      public Integer queryUserIdByUserName(String userName); } 

RoleDao:

public interface RoleDao {     public Role queryRoleListByRoleId(Integer roleId); }  

UserRoleDao:

public interface UserRoleDao {      public List<Integer> queryRoleIdByUserId(Integer userId); }  

PermissionDao:

public interface PermissionDao {      public Permission queryPermissionByPerId(Integer perId); }  

RolePerDao:

public interface RolePerDao {      public List<Integer> queryPerIdByRoleId(Integer roleId);  }  

UserMapper:

<resultMap id="userMap" type="com.wxy.shiro4.beans.User">     <id column="user_id" property="userId"/>     <result column="user_name" property="userName"/>     <result column="user_pwd" property="userPwd"/>     <result column="pwd_salt" property="pwdSalt"/> </resultMap>  <select id="queryUserByUserName" resultMap="userMap">     SELECT     *     FROM     USER     WHERE     user_name = #{userName} </select>  <select id="queryUserIdByUserName" resultType="integer">     SELECT     user_id     FROM     USER     WHERE     user_name = #{userName} </select> 

RoleMapper:

<resultMap id="roleMap" type="com.wxy.shiro4.beans.Role">     <id column="role_id" property="roleId"/>     <result column="role_name" property="roleName"/>     <result column="role_desc" property="roleDesc"/> </resultMap> <select id="queryRoleListByRoleId" resultMap="roleMap">     SELECT     *     FROM     role     WHERE     role_id = #{roleId} </select> 

UserRoleMapper:

<select id="queryRoleIdByUserId" resultType="integer">     SELECT     role_id     FROM     user_role     WHERE     user_id = #{userId} </select> 

PermissionMapper

<resultMap id="permissionMap" type="com.wxy.shiro4.beans.Permission">     <id column="per_id" property="perId"/>     <result column="permisson" property="permission"/>     <result column="per_desc" property="perDesc"/> </resultMap> <select id="queryPermissionByPerId" resultMap="permissionMap">     SELECT     *     FROM     permission     WHERE     per_id = #{perId} </select> 

RolePerMapper

<select id="queryPerIdByRoleId" resultType="integer">     SELECT     per_id     FROM     role_per     WHERE     role_id = #{roleId} </select> 

写完mapper之后,在启动类上加上@MapperScan(baseP<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>

基于java配置Shiro,ShiroConfig类

@Configuration public class ShiroConfig {      //Shiro的方言支持     @Bean     public ShiroDialect getShiroDialect() {         return new ShiroDialect();     }      //自定义Realm     @Bean     public MyRealm getMyRealm() {         MyRealm myRealm = new MyRealm();         return myRealm;     }      //SecurityManager(安全管理器)     @Bean     public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm) {         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();         //securityManager要完成校验需要realm的支持         securityManager.setRealm(myRealm);         return securityManager;     }      //过滤器     @Bean     public ShiroFilterFactoryBean filter(SecurityManager securityManager) {         ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();         //过滤器是Shiro         filterFactoryBean.setSecurityManager(securityManager);         //设置shiro的拦截规则         /*             anon    匿名用户可访问             authc   认证用户可访问             user    使用remenberme的用户可访问             perms   对应权限可访问             role    对应角色可访问          */         Map<String, String> filterMap = new HashMap<>();         filterMap.put("/","anon");         filterMap.put("/login.html","anon");         filterMap.put("/index.html","anon");         filterMap.put("/regist.html","anon");         filterMap.put("/user/login","anon");         filterMap.put("/user/regist","anon");         filterMap.put("/static/**","anon");         filterMap.put("/**","authc");          filterFactoryBean.setFilterChainDefinitionMap(filterMap);         filterFactoryBean.setLoginUrl("/login.html");         //设置未授权访问的页面路径         filterFactoryBean.setUnauthorizedUrl("/login.html");         return filterFactoryBean;     }   } 

自定义Realm,Realm对外提供合法数据。里面有两个方法,doGetAuthenticationInfo为认证器提供数据,doGetAuthorizationInfo为授权器提供数据。

可以自定义多个realm,只是要在ShiroConfig中使用securityManager.setRealms()来设置多个Realm。

/**  * 1.创建一个类继承AuthorizingRealm才能称为一个Realm(实现了Realm接口的类)  * 2.重写doGetAuthorizationInfo和doGetAuthenticationInfo两个方法  * 3.重写getName()方法,返回当前realm的一个自定义名称  */ public class MyRealm extends AuthorizingRealm {      @Resource     private UserDao userDao;     @Resource     private RoleDao roleDao;     @Resource     private UserRoleDao userRoleDao;     @Resource     private PermissionDao permissionDao;     @Resource     private RolePerDao rolePerDao;      @Override     public String getName() {         return "myRealm";     }      /**      * 获取授权数据(将当前用户的角色及权限信息查询出来)      * @param principalCollection      * @return      */     @Override     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {         //获取当前用户的用户名         String username = (String) principalCollection.iterator().next();         //根据用户名查询当前用户的角色列表         Integer userId = userDao.queryUserIdByUserName(username);         List<Integer> roleIds = userRoleDao.queryRoleIdByUserId(userId);         Set<String> roleNames = new HashSet<>();         Set<String> perNames = new HashSet<>();         for (Integer roleId : roleIds) {             Role role = roleDao.queryRoleListByRoleId(roleId);             roleNames.add(role.getRoleName());             List<Integer> perIds = rolePerDao.queryPerIdByRoleId(roleId);             //根据用户名查询当前用户的权限列表             for (Integer perId : perIds) {                 Permission permission = permissionDao.queryPermissionByPerId(perId);                 perNames.add(permission.getPermission());             }         }          SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();         info.setRoles(roleNames);         info.setStringPermissions(perNames);         return info;     }      /**      * 获取认证的安全数据(从数据库查询的用户的正确数据)      * @param authenticationToken      * @return      * @throws AuthenticationException      */     @Override     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {         //参数authenticationToken就是传递的 subject.login(token)中的token         UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;         //从token中获取用户名         String username = token.getUsername();         //根据用户名从数据库查询当前用户的安全数据         User user = userDao.queryUserByUserName(username);         if (user == null) {             return null;         }         //把查询出来的安全信息放到AuthenticationInfo中         AuthenticationInfo info = new SimpleAuthenticationInfo(                 username,           //当前用户用户名                 user.getUserPwd(),  //从数据库查询出来的安全密码                 getName()           //当前Realm名         );         return info;     }  }  

以上经测试可以成功,那么就实现了动态分配权限的效果。配合前端页面,可以实现不同用户登录看到不同页面的效果。

总结

在Spring Boot上使用Shiro其实非常方便,主要就是导入依赖,配置ShiroConfig,然后自定义Realm或者使用JDBCRealm;
使用JdbcRealm需要对数据库有严格要求,而自定义Realm只需要自己设计数据库就好了。
另外,我对于Shiro的理解为:Realm从数据库拿取合法数据,然后将数据提供给认证器和授权器,认证器和授权器拿到数据后返回给前台。
现在由于只是学习阶段,对这方面知识不够深入理解,以上只是我自己在看视频的时候的随笔笔记,其中部分思考都是我自己结合教学得出,如有错误,欢迎指正。
如有侵权,联系删除。

版权声明:玥玥 发表于 2021-03-16 4:04:54。
转载请注明:Shiro学习笔记之Shiro与Spring Boot的结合(两种方式) | 女黑客导航