Spring Boot入门教程(7)---整合jpa,Shiro进行权限管理(附源码)


声明:本文转载自https://my.oschina.net/u/1791512/blog/1548159,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

前面已经介绍过springBoot和mybatis、JPA的整合,本篇主要是在上一篇的基础上整合Shiro进行权限的管理。

源码链接

1、简介

先简单介绍下shiro吧,其实它就是一个安全框架,相比spring Security使用起来更简单易懂。引用一张架构图(图片来自官网),从图中大家可以看到他的三大核心:

 -Subject 当前用户操作 
- SecurityManager 用于管理所有的Subject 
- Realms 用于进行权限信息的验证,也是我们需要自己实现的。

我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
Apache Shiro 核心通过 Filter 来实现,就好像SpringMvc 通过DispachServlet 来主控制一样。 
既然是使用 Filter 一般也就能猜到,是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。
另外我们可以通过Shiro 提供的会话管理来获取Session中的信息。Shiro 也提供了缓存支持,使用 CacheManager 来管理。

要集成shiro我们必须知道他的几个核心对象,分别是:
第一:ShiroFilterFactory,Shiro过滤器工厂类,具体的实现类是:ShiroFilterFactoryBean,此实现类是依赖于SecurityManager安全管理器。
第二:SecurityManager,Shiro的安全管理,主要是身份认证的管理,缓存管理,cookie管理,所以在实际开发中我们主要是和SecurityManager进行打交道的,ShiroFilterFactory主要配置好了Filter就可以了。当然SecurityManager并进行身份认证缓存的实现,我们需要进行对应的编码然后进行注入到安全管理器中。
第三:Realm,用于身份信息权限信息的验证。
第四:其它的就是缓存管理,记住登录之类的,这些大部分都是需要自己进行简单的实现,然后注入到SecurityManager让Shiro的安全管理器进行管理就好了。

2、整合完成demo

概念介绍完了 ,我们开始动手,完成我们的demo,具体步骤如下:

(a) pom.xml中添加Shiro依赖;
(b) 注入Shiro Factory和SecurityManager。
(c) 身份认证
(d) 权限控制

(a) 添加Shiro依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 	<modelVersion>4.0.0</modelVersion> 	<groupId>com.pxk</groupId> 	<artifactId>SpringBootDemo_JPA</artifactId> 	<packaging>war</packaging> 	<version>0.0.1-SNAPSHOT</version> 	<name>SpringBootDemo_JPA Maven Webapp</name> 	<url>http://maven.apache.org</url> 	<build> 		<finalName>SpringBootDemo_JPA</finalName> 	</build> 	<parent> 		<groupId>org.springframework.boot</groupId> 		<artifactId>spring-boot-starter-parent</artifactId> 		<version>1.5.6.RELEASE</version> 		<relativePath /> 	</parent> 	<dependencies> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter</artifactId> 		</dependency> 		<!-- web容器 --> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-web</artifactId> 			<version>1.5.6.RELEASE</version> 		</dependency> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-data-jpa</artifactId> 		</dependency> 		<dependency> 			<groupId>org.springframework</groupId> 			<artifactId>spring-jdbc</artifactId> 			<version>4.3.10.RELEASE</version> 		</dependency> 		<dependency> 			<groupId>mysql</groupId> 			<artifactId>mysql-connector-java</artifactId> 			<version>5.1.40</version> 		</dependency> 		<!--日志 --> 		<dependency> 			<groupId>log4j</groupId> 			<artifactId>log4j</artifactId> 			<version>1.2.17</version> 		</dependency> 		<!-- druid连接池 --> 		<dependency> 			<groupId>com.alibaba</groupId> 			<artifactId>druid</artifactId> 			<version>1.0.18</version> 		</dependency> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-test</artifactId> 			<scope>test</scope> 		</dependency> 		<!-- shiro --> 		<dependency> 			<groupId>org.apache.shiro</groupId> 			<artifactId>shiro-spring</artifactId> 			<version>1.2.3</version> 		</dependency> 	</dependencies> </project> 

(b) 注入Shiro Factory和SecurityManager

    使用springBoot的配置方式,新建config类,主要配置两个类ShiroFilterFactory和SecurityManager

package com.pxk.springboot.config;  import java.util.LinkedHashMap; import java.util.Map; import org.apache.shiro.mgt.SecurityManager; 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;  @Configuration public class ShiroConfiguration {  	@Bean 	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { 		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 		// 必须设置SecuritManager 		shiroFilterFactoryBean.setSecurityManager(securityManager); 		// 拦截器 		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); 		// 配置退出过滤器,其中的具体代码Shiro已经替我们实现了 		filterChainDefinitionMap.put("/logout", "logout"); 		// <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了; 		// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> 		filterChainDefinitionMap.put("/**", "authc");  		// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 		shiroFilterFactoryBean.setLoginUrl("/login"); 		// 登录成功后要跳转的链接 		shiroFilterFactoryBean.setSuccessUrl("/index"); 		// 未授权界面; 		shiroFilterFactoryBean.setUnauthorizedUrl("/403");  		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 		return shiroFilterFactoryBean;  	}  	@Bean 	public SecurityManager securityManager() { 		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 		return securityManager; 	}  }

(c) 身份认证

     在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.
认证实现
Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法。
该方法主要执行以下操作:
1、检查提交的进行认证的令牌信息
2、根据令牌信息从数据源(通常为数据库)中获取用户信息
3、对用户信息进行匹配验证。
4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。
5、验证失败则抛出AuthenticationException异常信息。
而在我们的应用程序中要做的就是自定义一个Realm类,继承AuthorizingRealm抽象类,重载doGetAuthenticationInfo (),重写获取用户信息的方法。
既然需要进行身份权限控制,那么少不了创建用户实体类,权限实体类。
      在权限管理系统中,有这么几个角色很重要,这个要是不清楚的话,那么就很难理解,我们为什么这么编码了。第一是用户表:在用户表中保存了用户的基本信息,账号、密码、姓名,性别等;第二是:权限表(资源+控制权限):这个表中主要是保存了用户的URL地址,权限信息;第三就是角色表:在这个表重要保存了系统存在的角色;第四就是关联表:用户-角色管理表(用户在系统中都有什么角色,比如admin,vip等),角色-权限关联表(每个角色都有什么权限可以进行操作)。依据这个理论,我们进行来进行编码,很明显的我们第一步就是要进行实体类的创建。在这里我们使用Mysql和JPA进行操作数据库。

新建实体类UserInfo、SysRole、SysPermission(采用逆向工程生成数据库表,然后导入数据)--非关键的get、set方法省略

UserInfo.java

package com.pxk.springboot.domain;  import java.io.Serializable; import java.util.List;  import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany;  /**  * 用户信息.  *   * @author Administrator  *   */ @Entity public class UserInfo implements Serializable {  	/**   	 *    	 */ 	private static final long serialVersionUID = 1L;  	@Id 	@GeneratedValue 	private long uid;// 用户id  	@Column(unique = true) 	private String username;// 帐号  	private String name;// 名称(昵称或者真实姓名,不同系统不同定义)  	private String password; // 密码; 	private String salt;// 加密密码的盐  	private byte state;// 用户状态,0:创建未认证(比如没有激活,没有输入验证码等等)--等待验证的用户 , 						// 1:正常状态,2:用户被锁定.  	@ManyToMany(fetch = FetchType.EAGER) // 立即从数据库中进行加载数据 	@JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns = { 			@JoinColumn(name = "roleId") }) 	private List<SysRole> roleList;// 一个用户具有多个角色  	/** 	 * 密码盐. 	 *  	 * @return 	 */ 	public String getCredentialsSalt() { 		return this.username + this.salt; 	}  	@Override 	public String toString() { 		return "UserInfo [uid=" + uid + ", username=" + username + ", name=" + name + ", password=" + password 				+ ", salt=" + salt + ", state=" + state + "]"; 	}  } 

SysRole.java

package com.pxk.springboot.domain;  import java.io.Serializable; import java.util.List;  import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany;  /**  * 系统角色实体类;  *   * @author Administrator  *   */ @Entity public class SysRole implements Serializable { 	private static final long serialVersionUID = 1L; 	@Id 	@GeneratedValue 	private Long id; // 编号 	private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的: 	private String description; // 角色描述,UI界面显示使用 	private Boolean available = Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户  	// 角色 -- 权限关系:多对多关系; 	@ManyToMany(fetch = FetchType.EAGER) 	@JoinTable(name = "SysRolePermission", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = { 			@JoinColumn(name = "permissionId") }) 	private List<SysPermission> permissions;  	// 用户 - 角色关系定义; 	@ManyToMany 	@JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = { 			@JoinColumn(name = "uid") }) 	private List<UserInfo> userInfos;// 一个角色对应多个用户  	@Override 	public String toString() { 		return "SysRole [id=" + id + ", role=" + role + ", description=" + description + ", available=" + available 				+ ", permissions=" + permissions + "]"; 	} }

SysPermission.java

package com.pxk.springboot.domain;  import java.io.Serializable; import java.util.List;  import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany;  /**  * 权限实体类;  *   */ @Entity public class SysPermission implements Serializable { 	private static final long serialVersionUID = 1L;  	@Id 	@GeneratedValue 	private long id;// 主键. 	private String name;// 名称.  	@Column(columnDefinition = "enum('menu','button')") 	private String resourceType;// 资源类型,[menu|button] 	private String url;// 资源路径. 	private String permission; // 权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view 	private Long parentId; // 父编号 	private String parentIds; // 父编号列表 	private Boolean available = Boolean.FALSE;  	@Override 	public String toString() { 		return "SysPermission [id=" + id + ", name=" + name + ", resourceType=" + resourceType + ", url=" + url 				+ ", permission=" + permission + ", parentId=" + parentId + ", parentIds=" + parentIds + ", available=" 				+ available + "]"; 	}  }

MyShiroRealm.java

该类是实现权限认证的核心,需要我们手动实现

package com.pxk.springboot.config;  import javax.annotation.Resource;  import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; 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.apache.shiro.util.ByteSource;  import com.pxk.springboot.domain.SysPermission; import com.pxk.springboot.domain.SysRole; import com.pxk.springboot.domain.UserInfo; import com.pxk.springboot.serivce.UserInfoService;  /**  * 身份校验核心类  *   * @author Administrator  *   */ public class MyShiroRealm extends AuthorizingRealm {  	@Resource 	private UserInfoService userInfoService;  	/** 	 * 认证信息(身份验证) Authentication 是用来验证用户身份 	 */ 	@Override 	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 		System.out.println("MyShiroRealm.doGetAuthenticationInfo()"); 		// 获取用户的输入帐号 		String username = (String) token.getPrincipal(); 		System.out.println(token.getCredentials()); 		// 通过username从数据库中查找 User对象,如果找到,没找到. 		// 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法 		UserInfo userInfo = userInfoService.findByUsername(username); 		System.out.println("----->>userInfo=" + userInfo); 		if (userInfo == null) { 			return null; 		}  		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo, // 用户名 				userInfo.getPassword(), // 密码 				ByteSource.Util.bytes(userInfo.getCredentialsSalt()), // salt=username+salt 				getName() // realm name 		); 		return authenticationInfo; 	}  	@Override 	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 		// TODO Auto-generated method stub 		System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");  		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); 		UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();  		for (SysRole role : userInfo.getRoleList()) {  			authorizationInfo.addRole(role.getRole()); 			System.out.println(role.getPermissions()); 			for (SysPermission p : role.getPermissions()) { 				System.out.println(p); 				authorizationInfo.addStringPermission(p.getPermission()); 			} 		} 		return authorizationInfo; 	}  } 

核心功能基本完成,接下来就是编写controller和页面了

页面代码我就不详细贴出了,直接给大家源码地址进行下载

 

本文发表于2017年10月10日 14:34
(c)注:本文转载自https://my.oschina.net/u/1791512/blog/1548159,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2243 讨论 0 喜欢 1

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1