SpringBoot整合Shrio
添加maven的依赖
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xhrtas</groupId>
<artifactId>rtas</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rtas</name>
<description>RTAS数据库分析系统</description>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<skipTests>true</skipTests>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<!-- 排除springboot默认的logback日志框架 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入log4j日志依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--Spring MVC数据校验-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
<!-- apache 工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring-boot-web-starter -->
<!-- <dependency>-->
<!-- <groupId>org.apache.shiro</groupId>-->
<!-- <artifactId>shiro-spring-boot-web-starter</artifactId>-->
<!-- <version>1.4.0</version>-->
<!-- </dependency>-->
<!--poi读取excel-->
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<!--读取excel文件-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.10-FINAL</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.10-FINAL</version>
</dependency>
<!--Bouncy Caslte Provider-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.45</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.penggle/kaptcha -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.57</version>
</dependency>
<!--用于写入csv-->
<!-- https://mvnrepository.com/artifact/net.sourceforge.javacsv/javacsv -->
<dependency>
<groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId>
<version>2.0</version>
</dependency>
<!--shrio-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
<build>
<!-- <resources>-->
<!-- <resource>-->
<!-- <directory>src/main/java</directory>-->
<!-- <includes>-->
<!-- <include>**/mapper/*.xml</include>-->
<!-- <include>**/application.yml</include>-->
<!-- </includes>-->
<!-- </resource>-->
<!-- </resources>-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warName>rtas</warName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<!-- Force alphabetical order to have a reproducible build -->
<runOrder>alphabetical</runOrder>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
<!--这里相当于将第三方依赖在打包的时候,将第三方jar包放到了target下指定的lib文件夹里,但是没有打到jar包内。-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
<!-- 将第三方jar包一起并入到项目jar包中 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<archive>
<manifest>
<mainClass></mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- 指定在打包节点执行jar包合并操作 -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
springboot的yml配置文件
# REDIS (RedisProperties)
# redis-cli -h 47.92.253.74 -p 6379 -a xhu-rtas-redis
spring:
# profiles:
# active: @env@
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: 47.111.151.251
#host: 127.0.0.1
# Redis服务器连接端口,需要开通端口
port: 6379
# Redis服务器连接密码(默认为空)
password: xhu-rtas-redis
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
# 连接超时时间(毫秒)
timeout: 20000
server:
port: 8080
servlet:
context-path: /rtas
jdbc:
driver: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/rtas_shrio?useUnicode=true&charactorEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: root
password: 123456
#password: xhu-rtas-mysql
# mybatis
mybatis_config_file: mybatis-config.xml
mapper_path: mapper/**.xml
entity_package: com.xhrtas.rtas.repository.domain
# 线程池配置
executor:
core_pool_size: 5
max_pool_size: 10
queue_capacity: 1000
thread_name_prefix: async-service-
keep_alive_seconds: 5
# 图形验证码
kaptcha:
border: "yes"
border.color: 105,179,90
textproducer:
font:
color: blue
size: 30
names: 宋体,楷体,微软雅黑
char:
length: 4
image:
width: 120
height: 67
session:
key: code
#Shrio配置
shrio:
hashedCredentialsMatcher:
hashAlgorithmName: md5
hashIterations: 2
shiroFilter:
loginUrl: /user/authc
unauthorizedUrl: /user/authc
filterChainDefinitionMap:
anon:
image: /img/**
login: /user/login
static: /static/
authc:
other: /**
整合带有缓存的shrio
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
缓存对象存放路径
java.io.tmpdir:默认的临时文件存放路径。
user.home:用户的主目录。
user.dir:用户的当前工作目录,即当前程序所对应的工作路径。
其它通过命令行指定的系统属性,如“java –DdiskStore.path=D:\\abc ……”。
-->
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!-- 授权缓存 -->
<cache name="authorizationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<!-- 认证缓存 -->
<cache name="authenticationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
Shrio的配置
shrio配置
@Configuration
public class ShrioConfiguration {
/**
* 加密方式
*/
@Value("${shrio.hashedCredentialsMatcher.hashAlgorithmName}")
private String hashAlgorithmName;
/**
* 加密迭代次数
*/
@Value("${shrio.hashedCredentialsMatcher.hashIterations}")
private int hashIterations;
/**
* 登陆url
*/
@Value("${shrio.shiroFilter.loginUrl}")
private String loginUrl;
/**
* 认证失败的url
*/
@Value("${shrio.shiroFilter.unauthorizedUrl}")
private String unauthorizedUrl;
/**
* 放行image验证码
*/
@Value("${shrio.shiroFilter.filterChainDefinitionMap.anon.image}")
private String image;
/**
* 放行登陆方法
*/
@Value("${shrio.shiroFilter.filterChainDefinitionMap.anon.login}")
private String login;
/**
* 放行静态资源
*/
@Value("${shrio.shiroFilter.filterChainDefinitionMap.anon.static}")
private String staticStr;
/**
* 需要认证的资源
*/
@Value("${shrio.shiroFilter.filterChainDefinitionMap.authc.other}")
private String other;
/**
* 密码校验规则HashedCredentialsMatcher
* 这个类是为了对密码进行编码的 ,
* 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
* 这个类也负责对form里输入的密码进行编码
* 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为MD5
credentialsMatcher.setHashAlgorithmName(hashAlgorithmName);
//加密次数
credentialsMatcher.setHashIterations(hashIterations);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
@Bean("authRealm")
@DependsOn("lifecycleBeanPostProcessor")//可选
public AuthRealm authRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
AuthRealm authRealm = new AuthRealm();
authRealm.setCredentialsMatcher(matcher);
authRealm.setCachingEnabled(true);
//启用授权缓存,即缓存AuthorizationInfo信息,默认false
authRealm.setAuthorizationCachingEnabled(true);
//缓存AuthorizationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
authRealm.setAuthorizationCacheName("authorizationCache");
return authRealm;
}
/**
* 定义安全管理器securityManager,注入自定义的realm
* @param authRealm
* @return
*/
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//配置 ehcache缓存管理器
manager.setCacheManager(ehCacheManager());
manager.setRealm(authRealm);
return manager;
}
/**
* 定义shiroFilter过滤器并注入securityManager
* @param manager
* @return
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置securityManager
bean.setSecurityManager(manager);
//设置登录页面
//可以写路由也可以写jsp页面的访问路径
bean.setLoginUrl(loginUrl);
//设置登录成功跳转的页面
// bean.setSuccessUrl("/pages/index.jsp");
//设置未授权跳转的页面
bean.setUnauthorizedUrl(unauthorizedUrl);
//定义过滤器
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put(image, "anon");
filterChainDefinitionMap.put(login, "anon");
filterChainDefinitionMap.put(staticStr, "anon");
//需要登录访问的资源 , 一般将/**放在最下边
filterChainDefinitionMap.put(other, "authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
/**
* Spring的一个bean , 由Advisor决定对哪些类的方法进行AOP代理 .
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
/**
* 配置shiro跟spring的关联
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
/**
* lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
* (可选)
*/
@Bean("lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* shiro缓存管理器;
* 需要添加到securityManager中
* @return
*/
@Bean
public EhCacheManager ehCacheManager(){
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return cacheManager;
}
}
自定义Realm
自定义Realm包括对用户的认证、授权
package com.xhrtas.rtas.configuration.shrio;
import com.xhrtas.rtas.enums.ErrorCodeEnum;
import com.xhrtas.rtas.exception.BusinessException;
import com.xhrtas.rtas.repository.dao.UserDao;
import com.xhrtas.rtas.repository.domain.*;
import com.xhrtas.rtas.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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class AuthRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private UserDao userDao;
/**
* 为用户授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("授权========");
//获取前端输入的用户信息,封装为User对象
User userweb = (User) principals.getPrimaryPrincipal();
//获取前端输入的用户电话号码
String tel = userweb.getTelephone();
//根据前端输入的用户名查询数据库中对应的记录
User user = userDao.getByTelephone(tel);
//如果数据库中有该用户名对应的记录,就进行授权操作
if (user != null){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
UserRole userRole = user.getUserRole();
//为用户授予角色
info.addRole(userRole.getRole().getName());
//用户授予权限
Collection<String> permissionCollection = new HashSet<String>();
for(RolePermission rolePermission:userRole.getRole().getRolePermissionList()){
permissionCollection.add(rolePermission.getPermission().getName());
}
info.addStringPermissions(permissionCollection);
return info;
}else{
return null;
}
}
/**
* 认证登录
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证========");
//token携带了用户信息
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//获取前端输入的用户名
String tel = usernamePasswordToken.getUsername();
//根据用户名查询数据库中对应的记录
User user = userDao.getByTelephone(tel);
if (user == null){
throw new AuthenticationException();
}
//当前realm对象的name
String realmName = getName();
//封装用户信息,构建AuthenticationInfo对象并返回
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, user.getPassword(),ByteSource.Util.bytes(user.getSalt()), realmName);
return authcInfo;
}
/**
* 重写方法,清除当前用户的的 授权缓存
* @param principals
*/
@Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
/**
* 重写方法,清除当前用户的 认证缓存
* @param principals
*/
@Override
public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
super.clearCachedAuthenticationInfo(principals);
}
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
}
登陆
登陆操作,登陆成功后报错session
//初始化
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(telephone, password);
User user =null;
//登录,即身份校验,由通过Spring注入的UserRealm会自动校验输入的用户名和密码在数据库中是否有对应的值
try {
//4、登录,即身份验证
subject.login(token);
if(subject.isAuthenticated()){
user = (User)subject.getPrincipal();
request.getSession().setAttribute(SessionConstant.USER,user);
}
}catch (UnknownAccountException e) {
throw new BusinessException(ErrorCodeEnum.NO_USER);
} catch (IncorrectCredentialsException e) {
throw new BusinessException(ErrorCodeEnum.PASSWORD_ERROR);
} catch (AuthenticationException e) {
//其他错误,比如锁定,如果想单独处理请单独catch处理
throw new BusinessException(ErrorCodeEnum.NO_USER);
}
result.setUser(user);
request.getSession().setAttribute("user", user);
使用shrio
使用shrio可以有两种
1. 认证用户角色
2. 认证用户权限
认证用户角色
@PostMapping(value = "/nais")
@RequiresRoles(value = {"administrator","collector"},logical = Logical.OR)
public ResultVo readNais(@RequestParam(value = "file") MultipartFile file, HttpSession session) throws IOException {
//获取数据
PageDto<AccidentVo> accidentPageDto = naisService.readNais(file);
return ResultUtil.success(accidentPageDto);
}
其中的logical = Logical.OR代表两角色有一个就行
认证用户权限
@RequestMapping(value = "data")
@RequiresPermissions(value ={"total","nais"},logical = Logical.AND)
public void getData(){
}
其中的logical = Logical.AND代表两角色都有才就行