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
4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE com.ruochu ruochu 0.0.1-SNAPSHOT ruochu 人生若如初见,何事秋风悲画扇 1.8 1.3.2 2.5 1.2.5 1.4.0 2.0.0 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test mysql mysql-connector-java runtime org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis.boot.version} com.alibaba druid-spring-boot-starter RELEASE org.springframework.boot spring-boot-configuration-processor true ch.qos.logback logback-classic org.slf4j jcl-over-slf4j org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-devtools true org.apache.commons commons-lang3 com.github.pagehelper pagehelper-spring-boot-starter ${pagehelper.boot.version} org.apache.shiro shiro-core ${shiro.version} org.apache.shiro shiro-spring ${shiro.version} org.apache.shiro shiro-ehcache ${shiro.version} com.github.theborakompanioni thymeleaf-extras-shiro ${thymeleaf.extras.shiro.version} org.springframework.boot spring-boot-maven-plugin
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
配置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连接约束配置,即过滤链的定义 LinkedHashMapfilterChainDefinitiOnMap= 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 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/*"); MapinitParameters = 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中的用户 ListpermissiOns=new ArrayList<>(); Set roles = user.getRoles(); if(roles.size()>0) { for(SysRole role : roles) { Set 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 CacheloginRecordCache; @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; } }
成了