前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用户尝试登陆错误次数

用户尝试登陆错误次数

作者头像
微醺
发布2019-01-17 12:51:45
1.5K0
发布2019-01-17 12:51:45
举报
文章被收录于专栏:有脑子的搬砖工

1.引入依赖

本文主要引入的jar包如下:

代码语言:javascript
复制
<dependency>
	    <groupId>org.apache.shiro</groupId>
	    <artifactId>shiro-spring</artifactId>
	    <version>1.4.0</version>
	</dependency>
	<dependency>
	    <groupId>org.apache.shiro</groupId>
	    <artifactId>shiro-ehcache</artifactId>
	    <version>1.4.0</version>
	</dependency>

由于我们将使用shiro + ehache配合使用,所以可以不用单独再引用ehcache.jar了,使用shiro-ehcache时,会自动添加ehcache-core 2.6.11。

2.创建配置文件

resources config ehcache.xml

代码语言:javascript
复制
<?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"
	    dynamicConfig="true"
	    name="ehcache_retry">
	 
	    <!-- 磁盘缓存位置 -->
	    <diskStore path="java.io.tmpdir/ehcache" />
	 
	    <!-- 默认缓存 -->
	    <defaultCache maxEntriesLocalHeap="10000" 
	        eternal="false"
	        timeToIdleSeconds="120" 
	        timeToLiveSeconds="120" 
	        maxEntriesLocalDisk="10000000"
	        diskExpiryThreadIntervalSeconds="120" 
	        memoryStoreEvictionPolicy="LRU">
	        <persistence strategy="localTempSwap" />
	    </defaultCache>
	 
	    <!-- helloworld缓存 -->
	    <cache name="passwordRetryCache" 
	        maxElementsInMemory="2000" 
	        eternal="false"
	        timeToIdleSeconds="600" 
	        timeToLiveSeconds="600" 
	        overflowToDisk="false"
	        statistics="true" />

		<!--  
           设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域
           缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。
           如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>
           Hibernate 在不同的缓存区域保存不同的类/集合。
            对于类而言,区域的名称是类名。如:com.atguigu.domain.Customer
            对于集合而言,区域的名称是类名加属性名。如com.atguigu.domain.Customer.orders
       -->
       <!--  
           name: 设置缓存的名字,它的取值为类的全限定名或类的集合的名字 

           maxElementsInMemory: 设置基于内存的缓存中可存放的对象最大数目 
         
           eternal: 设置对象是否为永久的, true表示永不过期, 此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false 

          timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。 

           timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值 
        
           overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中 
       -->

	</ehcache>

关于以上属性代表的含义,可以在官方文档中找到官方文档 配置中不能对ehcache标签添加monitoring=“autodetect”,否侧缓存将无法保存。

3. MyShiroRealm.java

代码语言:javascript
复制
public class MyShiroRealm extends AuthorizingRealm {
    @Autowired
    private SysUserService sysUserService;

    //给当前realm起个名
    @Override
    public String getName() {
        return "customReam02";
    }
    //支持UsernamePasswordToken
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取用户主身份---用户名
        String username = (String) principalCollection.getPrimaryPrincipal();
        //通过用户名查找用户对应的权限列表
        List<SysPermission> permissionList = sysUserService.findPermission(username);
        //创建一个授权对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        for(SysPermission sysPermission:permissionList){
            authorizationInfo.addStringPermission(sysPermission.getPercode());
        }
        return authorizationInfo;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获取身份
        String username = (String) authenticationToken.getPrincipal();
        //通过用户名,查找对应的用户是否存在,如果存在返回用户对象
        SysUser sysUser = sysUserService.findUser(username);
        if(sysUser == null){
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                sysUser.getUsercode(), //用户名
                sysUser.getPassword(), //密码
                ByteSource.Util.bytes(sysUser.getSalt()),//salt
                getName()  //realm name
        );
        return authenticationInfo;
    }
}

4.RetryLimitCredentialsMatcher.java

代码语言:javascript
复制
public class RetryLimitCredentialsMatcher extends HashedCredentialsMatcher{
	    private static final int MAX_LOGIN_RETRY_TIMES = 5;
	    private Cache<String, AtomicInteger> passwordRetryCache;
		
	    public RetryLimitCredentialsMatcher(EhCacheManager ehCacheManager) {
	        passwordRetryCache = ehCacheManager.getCache("passwordRetryCache");
	    }
		
	    @Override
	    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws ExcessiveAttemptsException{
	        String userName = (String) token.getPrincipal();
	        AtomicInteger retryCount = passwordRetryCache.get(userName);
	        if (retryCount == null) {
	            // 高并发下使用的线程安全的int类
	            retryCount = new AtomicInteger(0);
	            passwordRetryCache.put(userName, retryCount);
	        }
	        if (retryCount.incrementAndGet() > MAX_LOGIN_RETRY_TIMES) {
	            throw new ExcessiveAttemptsException();
	        }
		
	        boolean match = super.doCredentialsMatch(token, info);
	        if (match) {
	            passwordRetryCache.remove(userName);
	        }
			
	        return match;
	    }
	}

这个类的主要作用就是计算并缓存用户尝试登陆的次数,如果大于了5次,那么该用户将被禁止登陆直到10分钟以后。这个时间在ehcache.xml中timeToIdleSeconds设置。

5.ShiroConfig.java

代码语言:javascript
复制
//配置文件注解
@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        //filterChainDefinitionMap.put("/userInfo/userList", "userInfo:view");
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");

        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 凭证匹配器
     * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * )
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }

    @Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        //myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        //匹配新创建管理匹配器
        myShiroRealm.setCredentialsMatcher(retryLimitCredentialsMatcher());
        return myShiroRealm;
    }


    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    /**
     *  开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean(name="simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver
    createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
        mappings.setProperty("UnauthorizedException","403");
        r.setExceptionMappings(mappings);  // None by default
        r.setDefaultErrorView("error");    // No default
        r.setExceptionAttribute("ex");     // Default is "exception"
        //r.setWarnLogCategory("example.MvcLogger");     // No default
        return r;
    }

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache.xml");
        return ehCacheManager;
    }

    @Bean
    public CredentialsMatcher retryLimitCredentialsMatcher() {
        RetryLimitCredentialsMatcher retryLimitCredentialsMatcher = new RetryLimitCredentialsMatcher(this.ehCacheManager());
        retryLimitCredentialsMatcher.setHashAlgorithmName("md5");
        retryLimitCredentialsMatcher.setHashIterations(2);
        retryLimitCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return retryLimitCredentialsMatcher;
    }
}

6.SpringEhcacheShutdownListener.java

代码语言:javascript
复制
@Component
public class SpringEhcacheShutdownListener implements ApplicationListener<ApplicationEvent>{
 
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextClosedEvent) {
            ApplicationContext context = ((ContextClosedEvent) event).getApplicationContext();
            EhCacheManager ehCacheManager = (EhCacheManager) context.getBean("ehCacheManager");
            if (ehCacheManager != null) {
                ehCacheManager.destroy();
            }
        }
    }
}
配置每次登出或者重启之后,清空缓存

7. HomeController

代码语言:javascript
复制
@Controller
public class HomeController {

    @RequestMapping({"/","/index"})
    public String index(){
        return "index";
    }

    @RequestMapping("/login")
    public String login(HttpServletRequest request, Map<String, Object> map) throws Exception{
        System.out.println("HomeController.login()");
        // 登录失败从request中获取shiro处理的异常信息。
        // shiroLoginFailure:就是shiro异常类的全类名.
        String exception = (String) request.getAttribute("shiroLoginFailure");
        System.out.println("exception=" + exception);
        String msg = "";
        if (exception!= null) {
            if (UnknownAccountException.class.getName().equals(exception)) {
                System.out.println("UnknownAccountException -- > 账号不存在:");
                msg = "UnknownAccountException -- > 账号不存在:";
            } else if (IncorrectCredentialsException.class.getName().equals(exception)) {
                System.out.println("IncorrectCredentialsException -- > 密码不正确:");
                msg = "IncorrectCredentialsException -- > 密码不正确:";
            } else if ("kaptchaValidateFailed".equals(exception)) {
                System.out.println("kaptchaValidateFailed -- > 验证码错误");
                msg = "kaptchaValidateFailed -- > 验证码错误";
            }else if (ExcessiveAttemptsException.class.getName().equals(exception)) {
                System.out.println("ExcessiveAttemptsException -- >  密码错误次数过多,已被锁定,请稍后重试");
                msg = "ExcessiveAttemptsException -- > 密码错误次数过多,已被锁定,请稍后重试";
            } else {
                msg = "else >> "+exception;
                System.out.println("else -- >" + exception);
            }
        }
        map.put("msg", msg);
        // 此方法不处理登录成功,由shiro进行处理
        return "login";
    }

    @RequestMapping("/403")
    public String unauthorizedRole(){
        System.out.println("------没有权限-------");
        return "403";
    }

}

测试

连续登陆你设置的次数,就会报登录次数太多

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年12月25日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.引入依赖
  • 2.创建配置文件
  • 3. MyShiroRealm.java
  • 4.RetryLimitCredentialsMatcher.java
  • 5.ShiroConfig.java
  • 6.SpringEhcacheShutdownListener.java
  • 7. HomeController
  • 测试
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档