1、Shiro是Apache下的一个开源项目,我们称之为Apache Shiro。它是一个很易用与Java项目的的安全框架,提供了认证、授权、加密、会话管理,与spring Security 一样都是做一个权限的安全框架,但是与Spring Security 相比,在于 Shiro 使用了比较简单易懂易于使用的授权方式。shiro属于轻量级框架,相对于security简单的多,也没有security那么复杂。所以我这里也是简单介绍一下shiro的使用。
2、非常简单;其基本功能点如下图所示:
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
好了 下面直接上代码POM
<?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.ruochu</groupId>
<artifactId>ruochu</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ruochu</name>
<description>人生若如初见,何事秋风悲画扇</description>
<properties>
<java.version>1.8</java.version>
<mybatis.boot.version>1.3.2</mybatis.boot.version>
<commons.io.version>2.5</commons.io.version>
<pagehelper.boot.version>1.2.5</pagehelper.boot.version>
<shiro.version>1.4.0</shiro.version>
<thymeleaf.extras.shiro.version>2.0.0</thymeleaf.extras.shiro.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SpringBoot集成mybatis框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.boot.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>
spring-boot-configuration-processor
</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!-- SpringBoot集成thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.boot.version}</version>
</dependency>
<!--Shiro核心框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- Shiro使用Srping框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- Shiro使用EhCache缓存框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- thymeleaf模板引擎和shiro框架的整合 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>${thymeleaf.extras.shiro.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml
# 项目相关配置
ruochu:
# 名称
name: RuoChu
# 版本
version: 1.0.0
# 版权年份
copyrightYear: 2019
# 文件上传路径
profile: D:/profile/
# 获取ip地址开关
addressEnabled: true
# 开发环境配置
server:
# 服务端口
port: 80
servlet:
# 项目contextPath
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# tomcat最大线程数,默认为200
max-threads: 800
# Tomcat启动初始化的线程数,默认值25
min-spare-threads: 30
# 日志配置
logging:
level:
com:
winterchen:
dao: DEBUG
#mybatis配置
mybatis:
#配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/rc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password:
# 连接池配置
# 初始化大小,最小,最大
initial-size: 1
max-active: 20
min-idle: 1
# 配置获取连接等待超时的时间
max-wait: 10000
#是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
pool-prepared-statements: true
#要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
max-open-prepared-statements: 20
#来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
validation-query: SELECT 1 FROM DUAL
validation-query-timeout: 5000
#申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
test-on-borrow: false
#归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
test-on-return: false
#建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
test-while-idle: true
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 30000
max-evictable-idle-time-millis: 60000
removeAbandoned: true
removeAbandonedTimeout: 1800
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
max-pool-prepared-statement-per-connection-size: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall #filters: #配置多个英文逗号分隔(统计,sql注入,log4j过滤)
type: com.alibaba.druid.pool.DruidDataSource
# 模板引擎
thymeleaf:
mode: HTML
encoding: utf-8
prefix: classpath:/templates/
suffix: .html
# 禁用缓存
cache: false
#热部署
devtools:
restart:
enabled: true
additional-paths: src/main/java
exclude: templates/**
# Shiro
shiro:
user:
# 登录地址
loginUrl: /
# 权限认证失败地址
unauthorizedUrl: /unauth
# 首页地址
indexUrl: /index
# 验证码开关
captchaEnabled: true
# 验证码类型 math 数组计算 char 字符
captchaType: math
cookie:
# 设置Cookie的域名 默认空,即当前访问的域名
domain:
# 设置cookie的有效访问路径
path: /
# 设置HttpOnly属性
httpOnly: true
# 设置Cookie的过期时间,天为单位
maxAge: 30
session:
# Session超时时间(默认30分钟)
expireTime: 30
# 同步session到数据库的周期(默认1分钟)
dbSyncPeriod: 1
# 相隔多久检查一次session的有效性,默认就是10分钟
validationInterval: 10
# 用户配置
user:
password:
# 密码错误{maxRetryCount}次锁定10分钟
maxRetryCount: 5
/ehcache-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="ruochu" updateCheck="false">
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir"/>
<!-- 默认缓存 -->
<defaultCache
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="false">
</defaultCache>
<!-- 登录记录缓存 锁定10分钟 -->
<cache name="passwordRetryCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
配置shiro
package com.ruochu.config;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruochu.common.utils.StringUtils;
import com.ruochu.shiro.AuthRealm;
import com.ruochu.shiro.CredentialMatcher;
import com.ruochu.shiro.web.filter.LogoutFilter;
/**
* 权限配置加载
*
* @author ruochu
*/
@Configuration
public class ShiroConfig
{
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
// Session超时时间,单位为毫秒(默认30分钟)
@Value("${shiro.session.expireTime}")
private int expireTime;
// 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟
@Value("${shiro.session.validationInterval}")
private int validationInterval;
// 验证码开关
@Value("${shiro.user.captchaEnabled}")
private boolean captchaEnabled;
// 验证码类型
@Value("${shiro.user.captchaType}")
private String captchaType;
// 设置Cookie的域名
@Value("${shiro.cookie.domain}")
private String domain;
// 设置cookie的有效访问路径
@Value("${shiro.cookie.path}")
private String path;
// 设置HttpOnly属性
@Value("${shiro.cookie.httpOnly}")
private boolean httpOnly;
// 设置Cookie的过期时间,秒为单位
@Value("${shiro.cookie.maxAge}")
private int maxAge;
// 登录地址
@Value("${shiro.user.loginUrl}")
private String loginUrl;
// 权限认证失败地址
@Value("${shiro.user.unauthorizedUrl}")
private String unauthorizedUrl;
@Value(value = "${user.password.maxRetryCount}")
private int maxRetryCount;
/**
* 缓存管理器 使用Ehcache实现
*/
@Bean
public EhCacheManager getEhCacheManager()
{
net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruochu");
EhCacheManager em = new EhCacheManager();
if (StringUtils.isNull(cacheManager))
{
em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
return em;
}
else
{
em.setCacheManager(cacheManager);
return em;
}
}
/**
* 自定义Realm
*/
@Bean
public AuthRealm userRealm(EhCacheManager cacheManager,@Qualifier("credentialMatcher") CredentialMatcher matcher)
{
AuthRealm userRealm = new AuthRealm();
userRealm.setCacheManager(cacheManager);
userRealm.setCredentialsMatcher(matcher);
return userRealm;
}
@Bean("credentialMatcher")
public CredentialMatcher credentialMatcher() {
return new CredentialMatcher();
}
/**
* 安全管理器
*/
@Bean
public SecurityManager securityManager(AuthRealm userRealm)
{
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(userRealm);
// 记住我
securityManager.setRememberMeManager(rememberMeManager());
// 注入缓存管理器;
securityManager.setCacheManager(getEhCacheManager());
// session管理器
//securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 退出过滤器
*/
public LogoutFilter logoutFilter()
{
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setLoginUrl(loginUrl);
return logoutFilter;
}
/**
* Shiro过滤器配置
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Shiro的核心安全接口,这个属性是必须的
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 身份认证失败,则跳转到登录页面的配置
shiroFilterFactoryBean.setLoginUrl(loginUrl);
// 权限认证失败,则跳转到指定页面
shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
// Shiro连接约束配置,即过滤链的定义
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 对静态资源设置匿名访问
filterChainDefinitionMap.put("/favicon.ico**", "anon");
filterChainDefinitionMap.put("/ruoyi.png**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/docs/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/ajax/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/ruoyi/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
// 退出 logout地址,shiro去清除session
filterChainDefinitionMap.put("/logout", "logout");
// 不需要拦截的访问
filterChainDefinitionMap.put("/login", "anon");
// 系统权限列表
// filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());
Map<String, Filter> filters = new LinkedHashMap<>();
// 注销成功,则跳转到指定页面
filters.put("logout", logoutFilter());
shiroFilterFactoryBean.setFilters(filters);
// 所有请求需要认证
//filterChainDefinitionMap.put("/**", "user,onlineSession,syncOnlineSession");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* cookie 属性设置
*/
public SimpleCookie rememberMeCookie()
{
SimpleCookie cookie = new SimpleCookie("rememberMe");
cookie.setDomain(domain);
cookie.setPath(path);
cookie.setHttpOnly(httpOnly);
cookie.setMaxAge(maxAge * 24 * 60 * 60);
return cookie;
}
/**
* 记住我
*/
public CookieRememberMeManager rememberMeManager()
{
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ=="));
return cookieRememberMeManager;
}
/**
* 开启Shiro注解通知器
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
@Qualifier("securityManager") SecurityManager securityManager)
{
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
}
配置druid
package com.ruochu.config;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
@Configuration
public class DruidConfiguration {
private static final Logger logger = LoggerFactory.getLogger(DruidConfiguration.class);
@Value("${spring.datasource.druid.url}")
private String dbUrl;
@Value("${spring.datasource.druid.username}")
private String username;
@Value("${spring.datasource.druid.password}")
private String password;
@Value("${spring.datasource.druid.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.druid.initial-size}")
private int initialSize;
@Value("${spring.datasource.druid.min-idle}")
private int minIdle;
@Value("${spring.datasource.druid.max-active}")
private int maxActive;
@Value("${spring.datasource.druid.max-wait}")
private int maxWait;
@Value("${spring.datasource.druid.time-between-eviction-runs-millis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.druid.min-evictable-idle-time-millis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.validation-query}")
private String validationQuery;
@Value("${spring.datasource.druid.test-while-idle}")
private boolean testWhileIdle;
@Value("${spring.datasource.druid.test-on-borrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.druid.test-on-return}")
private boolean testOnReturn;
@Value("${spring.datasource.druid.pool-prepared-statements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.druid.max-pool-prepared-statement-per-connection-size}")
private int maxPoolPreparedStatementPerConnectionSize;
@Value("${spring.datasource.druid.filters}")
private String filters;
@Value("{spring.datasource.druid.connection-properties}")
private String connectionProperties;
@Bean //声明其为Bean实例
@Primary //在同样的DataSource中,首先使用被标注的DataSource
public DataSource dataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
//configuration
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
try {
datasource.setFilters(filters);
} catch (Exception e) {
logger.error("druid configuration initialization filter", e);
}
datasource.setConnectionProperties(connectionProperties);
return datasource;
}
@Bean
public ServletRegistrationBean druidServlet() {
//logger.info("init Druid Servlet Configuration ");
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
Map<String, String> initParameters = new HashMap<>();
initParameters.put("loginUsername", "admin");// 用户名
initParameters.put("loginPassword", "123456");// 密码
initParameters.put("resetEnable", "false");// 禁用HTML页面上的“Reset All”功能
initParameters.put("allow", ""); // IP白名单 (没有配置或者为空,则允许所有访问)
//initParameters.put("deny", "192.168.20.38");// IP黑名单 (存在共同时,deny优先于allow)
servletRegistrationBean.setInitParameters(initParameters);
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean druidWebStatViewFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new WebStatFilter());
registrationBean.addInitParameter("urlPatterns", "/*");
registrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
return registrationBean;
}
}
自定义Realm
package com.ruochu.shiro;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.ruochu.model.SysModule;
import com.ruochu.model.SysRole;
import com.ruochu.model.SysUser;
import com.ruochu.service.ISysUserService;
import com.ruochu.shiro.service.SysPasswordService;
import com.ruochu.web.exception.user.CaptchaException;
import com.ruochu.web.exception.user.RoleBlockedException;
import com.ruochu.web.exception.user.UserBlockedException;
import com.ruochu.web.exception.user.UserNotExistsException;
import com.ruochu.web.exception.user.UserPasswordNotMatchException;
import com.ruochu.web.exception.user.UserPasswordRetryLimitExceedException;
/**
* 自定义Realm 处理登录 权限
*
* @author ruochu
*/
public class AuthRealm extends AuthorizingRealm{
private final static Logger log = LoggerFactory.getLogger(AuthRealm.class);
@Autowired
private ISysUserService userService;
@Autowired
private SysPasswordService passwordService;
//认证.登录
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException{
/* UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
String password = "";
if (upToken.getPassword() != null)
{
password = new String(upToken.getPassword());
}
SysUser user = userService.findByLoginName(username);
boolean passwordflag= passwordService.matches(user, password);
if(passwordflag) {
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
}else {
logger.info("对用户[" + username + "]进行登录验证..验证未通过{}");
throw new AuthenticationException("对用户[" + username + "]进行登录验证..验证未通过{}");
}*/
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
String password = "";
SimpleAuthenticationInfo info =null;
if (upToken.getPassword() != null)
{
password = new String(upToken.getPassword());
}
SysUser user = null;
try
{
user = userService.findByLoginName(username);
}
catch (Exception e)
{
log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
throw new AuthenticationException(e.getMessage(), e);
}
try {
info = new SimpleAuthenticationInfo(user, password, getName());
} catch (Exception e) {
log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
throw new AuthenticationException(e.getMessage(), e);
}
return info;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
SysUser user=(SysUser) principal.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
List<String> permissions=new ArrayList<>();
Set<SysRole> roles = user.getRoles();
if(roles.size()>0) {
for(SysRole role : roles) {
Set<SysModule> modules = role.getModules();
if(modules.size()>0) {
for(SysModule module : modules) {
permissions.add(module.getMname());
}
}
}
}
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addStringPermissions(permissions);//将权限放入shiro中.
return info;
}
}
package com.ruochu.shiro;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import com.ruochu.model.SysUser;
import com.ruochu.service.ISysUserService;
import com.ruochu.shiro.service.SysPasswordService;
import com.ruochu.web.exception.user.UserPasswordNotMatchException;
public class CredentialMatcher extends SimpleCredentialsMatcher {
/* * 这里是进行密码匹配的方法,自己定义
* 通过用户的唯一标识得到 AuthenticationInfo 然后和 AuthenticationToken (用户名 密码),进行比较
* 登陆失败次数统计
* */
private final static Logger log = LoggerFactory.getLogger(SysPasswordService.class);
@Autowired
private CacheManager cacheManager;
@Autowired
private ISysUserService userService;
@Autowired
private SysPasswordService passwordService;
private Cache<String, AtomicInteger> loginRecordCache;
@Value(value = "${user.password.maxRetryCount}")
private String maxRetryCount;
@PostConstruct
public void init()
{
loginRecordCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String loginName = usernamePasswordToken.getUsername();
AtomicInteger retryCount = loginRecordCache.get(loginName);
if (retryCount == null)
{
retryCount = new AtomicInteger(0);
loginRecordCache.put(loginName, retryCount);
}
if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue())
{
log.warn("用户[{}]进行登录验证..失败验证超过{}次", loginName, maxRetryCount);
throw new ExcessiveAttemptsException("username: " + loginName + " tried to login more than 5 times in period");
}
SysUser user = userService.findByLoginName(loginName);
if (!passwordService.matches(user, new String(usernamePasswordToken.getPassword())))
{
loginRecordCache.put(loginName, retryCount);
throw new UserPasswordNotMatchException();
}
else
{
passwordService.clearLoginRecordCache(loginName);
}
/*String password = new String(usernamePasswordToken.getPassword());
String dbPassword = (String) info.getCredentials();//数据库里的密码
return this.equals(password, dbPassword); */
return false;
}
}
成了