环境搭建
创建Maven工程,导入坐标
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!--spring和aspectj框架整合的模块-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!--spring支持jdbc编程模块-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.20</version>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--mybatis和spring整合依赖包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--spring mvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!--java web -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!--spring-json依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
来看下之前我们创建的ShiroUtil,然后把它通过spring ioc容器进行管理
//1.初始化shiro的安全管理器
DefaultSecurityManager securityManager=new DefaultSecurityManager();
<bean name="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
</bean>
Realm realm=new ShiroRealm();
<!--自定义的realm,交给spring ioc容器来管理-->
<bean name="realm" class="com.hsy.sys.shiro.ShiroRealm"/>
//使用第3方缓存创建缓存管理器
CacheManager cacheManager=new EhCacheManager();
<!--缓存管理器cacheManager,交给spring ioc容器来管理-->
<bean name="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"/>
设置用户的权限信息到安全管理器/缓存管理器到安全管理器中
<!--将安全管理器交给spring ioc容器来管理-->
<bean name="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
<!--注入自定义realm-->
<property name="realm" ref="realm"/>
<!--注入缓存管理器-->
<property name="cacheManager" ref="cacheManager"/>
</bean>
使用SecurityUtils将securityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
<!--
使用SecurityUtils将securityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
-->
<!--相当于调用SecurityUtils.setSecurityManager(securityManager)-->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
接下来把上面汇总到配置文件shiro-config.xml中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--自定义的realm,交给spring ioc容器来管理-->
<bean name="realm" class="com.hsy.sys.shiro.ShiroRealm"/>
<!--缓存管理器cacheManager,交给spring ioc容器来管理-->
<bean name="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"/>
<!--将安全管理器交给spring ioc容器来管理-->
<bean name="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
<!--注入自定义realm-->
<property name="realm" ref="realm"/>
<!--注入缓存管理器-->
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!--
使用SecurityUtils将securityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
-->
<!--相当于调用SecurityUtils.setSecurityManager(securityManager)-->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
<!--Shiro生命周期处理器-->
<bean name="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
当然别忘了spring的配置文件,上面配置文件也可以写在spring配置文件中,但是考虑代码复用,结构清晰,因此上述单独建立了一个配置文件,接下来只需在spring配置文件中引用就OK。
配置文件spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--导入shiro配置-->
<import resource="classpath:shiro-config.xml"/>
</beans>
接下来我们进行一下测试,还是前几篇文章我们所打造的场景,现在对测试类进行如下修改
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config.xml")
public class ShiroTest {
@Autowired
private SecurityManager securityManager;
@Test
public void test(){
//模拟从客户端接收到用户登录输入的用户名和密码(明文)
String name="zhangsan";
String password="123456";
UsernamePasswordToken token=new UsernamePasswordToken(name,password);
Subject subject= SecurityUtils.getSubject();
subject.login(token);
subject.isPermitted("sys:user:list");
}
}
测试
可以看到我们的环境搭建已经成功,接下来就说shiro集成spring之认证、授权
shiro集成spring之认证、授权
工作准备数据库
DROP TABLE IF EXISTS `sys_resource`;
CREATE TABLE `sys_resource` (
`resource_id` int(11) NOT NULL,
`parent_id` int(11) NOT NULL COMMENT '父资源ID,一级资源ID为0',
`name` varchar(50) NOT NULL,
`url` varchar(200) default NULL,
`permission` varchar(500) default NULL COMMENT '资源对应的权限的名称',
`type` int(11) NOT NULL COMMENT '0:目录 1:菜单 2:按钮',
`icon` varchar(50) default NULL,
`ord_num` int(11) default NULL COMMENT '统计目录谁前谁后,排个序',
`create_time` datetime default NULL,
`update_time` datetime default NULL,
PRIMARY KEY (`resource_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`role_id` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
`remark` varchar(100) default NULL COMMENT '角色的描述',
`dept_id` int(20) default NULL COMMENT '部门',
`create_time` datetime default NULL,
`update_time` datetime default NULL,
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `sys_role_resource`;
CREATE TABLE `sys_role_resource` (
`id` int(20) NOT NULL,
`role_id` int(20) NOT NULL,
`resource_id` int(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `role_id` USING BTREE (`role_id`,`resource_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`user_id` int(11) NOT NULL,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`salt` varchar(32) NOT NULL,
`email` varchar(100) default NULL,
`mobile` varchar(100) default NULL,
`dept_id` int(20) NOT NULL,
`status` int(11) NOT NULL default '0' COMMENT '状态 0:正常 1:禁用 2:锁定',
`deleted` int(11) NOT NULL default '0' COMMENT '0正常 1删除',
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`id` int(20) NOT NULL,
`user_id` int(20) NOT NULL,
`role_id` int(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` USING BTREE (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
使用mybatis逆向工程造出代码
application.properties代码
jdbc.username=root
jdbc.password=123
jdbc.url=jdbc:mysql://127.0.0.1:3306/shiro_spring?useSSL=false&serverTimezone=Asia/shanghai
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.initialSize=5
#文件服务器的地址
file.server=http://localhost:8080/admin/upload
#文件上传的路径
file.location=/upload
generatorConfig.xml代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<properties resource="application.properties"/>
<context id="mysql" targetRuntime="MyBatis3">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!--为了防止生成的代码中有很多注释,比较难看,加入下面的配置控制-->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
<property name="suppressDate" value="true"/>
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码-->
<jdbcConnection driverClass="${jdbc.driverClassName}"
connectionURL="${jdbc.url}"
userId="${jdbc.username}"
password="${jdbc.password}">
</jdbcConnection>
<!--targetProject:生成model类的位置-->
<javaModelGenerator targetPackage="com.hsy.sys.model" targetProject=".\src\main\java"/>
<!--targetProject:生成mapper映射文件的位置-->
<javaModelGenerator targetPackage="mapper.sys" targetProject=".\src\main\resources"/>
<!--targetProject:生成mapper接口位置-->
<javaModelGenerator targetPackage="com.hsy.sys.mapper" targetProject=".\src\main\java" type="XMLMAPPER"/>
<!--指定数据库表-->
<table tableName="sys_user" domainObjectName="User"/>
<table tableName="sys_role" domainObjectName="Role"/>
<table tableName="sys_user_role" domainObjectName="UserRole"/>
<table tableName="sys_resource " domainObjectName="Resource"/>
<table tableName="sys_role_resource" domainObjectName="RoleResource"/>
</context>
</generatorConfiguration>
接下来我们对ShiroRealm进行改造
认证
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
/**
* 登录认证
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 1.获取用户输入的用户名
String username = token.getUsername();
// 2.获取用户输入的密码
String password = new String(token.getPassword());
// 3.根据用户名去DB查询对应的用户信息
QueryWrapper<User> param = new QueryWrapper<User>();
param.eq("username",username);
User user = userMapper.selectOne(param);
if(user == null) {
throw new UnknownAccountException("用户名不存在");
}
password = MD5Util.md5_private_salt(password,user.getSalt());
// 两个密码的密文进行比对
if (!user.getPassword().equals(password)) {
throw new CredentialsException("密码错误");
}
if (user.getStatus() == 1) {
throw new DisabledAccountException("账号被禁用");
}
if (user.getStatus() == 2) {
throw new LockedAccountException("账号被锁定");
}
System.out.println("认证成功...");
// 创建简单认证信息对象
SimpleAuthenticationInfo info =
new SimpleAuthenticationInfo(user, token.getCredentials(), getName());
return info;
}
进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config.xml")
public class ShiroTest {
@Autowired
private SecurityManager securityManager;
@Test
public void test(){
//模拟从客户端接收到用户登录输入的用户名和密码(明文)
String name="zhangsan";
String password="123456";
UsernamePasswordToken token=new UsernamePasswordToken(name,password);
Subject subject= SecurityUtils.getSubject();
subject.login(token);
}
}
/**
* 授权
* 将认证通过的用户的角色和权限信息设置到对应用户主体上
*
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
// 简单授权信息对象,对象中包含用户的角色和权限信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//从数据库获取当前用户的角色 通过用户名查询该用户拥有的角色名称
Set<String> roleNameSet = userMapper.selectUserRoleNameSet(user.getUserId());
info.addRoles(roleNameSet);
//从数据库获取当前用户的权限 通过用户名查询该用户拥有的权限名称
Set<String> permissionNameSet = userMapper.selectUserPermissionNameSet(user.getUserId());
Set<String> permissions = new HashSet<String>();
for(String name : permissionNameSet) {
for(String permission : name.split(",")){
permissions.add(permission);
}
}
info.addStringPermissions(permissions);
System.out.println("授权完成....");
return info;
}
对测试类进行改造,测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config.xml")
public class ShiroTest {
@Autowired
private SecurityManager securityManager;
@Test
public void test(){
//模拟从客户端接收到用户登录输入的用户名和密码(明文)
String name="zhangsan";
String password="123456";
UsernamePasswordToken token=new UsernamePasswordToken(name,password);
Subject subject= SecurityUtils.getSubject();
subject.login(token);
System.out.println(subject.hasRole("系统管理员"));
System.out.println(subject.hasRole("网站运维"));
System.out.println(subject.hasRole("33333"));
System.out.println("-------------------------");
System.out.println(subject.isPermitted("sys:user:list"));
System.out.println(subject.isPermitted("sys:user:update"));
System.out.println(subject.isPermitted("sys:user:333dsf"));
}
}
上文链接:https://haosy.blog.csdn.net/article/details/103647880
Shiro系列专题链接:https://blog.csdn.net/qq_43518645/category_9604248.html
2019/12/22学习记录。