cas相关问题

上个礼拜搞安全问题,搞了几天,大部分是关于登录、账密、权限问题,现在做一个简单的总结

一,CAS

我这里是要在CAS里面做一个‘密码错误5次就锁定30分钟’。因为时刚拿到CAS,不太熟悉。问了一下同事,同事说好像CAS支持配置,不过,

在网上看了一下,好像是有配置,分单节点,和多节点。但是%#%¥¥……%*&……,然后我自己是实现。

一般来说,做这种需求,需要在数据库新增字段,例如‘最后登录时间’-->用来与锁定的时间对比,还有‘是否锁定’-->isLock

我所知道的是,CAS时直接用jdbcTemplate操作数据库的,在QueryDatabaseAuthenticationHandler 这里根据用户名查到密码(dbPassword),然后返回boolean值

 protected final boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) throws AuthenticationException {
        final String username = getPrincipalNameTransformer().transform(credentials.getUsername());
        final String password = credentials.getPassword();
        final String encryptedPassword = this.getPasswordEncoder().encode(
            password);
        
        try {
            final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, username);
            return dbPassword.equals(encryptedPassword);
        } catch (final IncorrectResultSizeDataAccessException e) {
            // this means the username was not found.
            return false;
        }
    }

也可以在这里查其他字段,因为是根据sql直接查的,所有要在deployerConfigContext.xml里面配一下sql

<bean  class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
                   <property name="dataSource" ref="dataSource" />
                   <property name="sql" value="select password from p_user where username=?" />
//需要对象的话,先封装一个对象(但我还不知道CAS是咋映射到数据库的) <property name="passwordEncoder" ref="passwordEncoderBean"/> </bean>

如果使用数据库做这个需求的话,可以参考上面的

如果是用缓存做的话就简单多了,贴代码

 protected Pair<AuthenticationHandler, Principal> authenticateAndObtainPrincipal(final Credentials credentials) throws AuthenticationException {
        boolean foundSupported = false;
        boolean authenticated = false;
        AuthenticationHandler authenticatedClass = null;
        String handlerName;
        
        for (final AuthenticationHandler authenticationHandler : this.authenticationHandlers) {
            if (authenticationHandler.supports(credentials)) {
                foundSupported = true;
                handlerName = authenticationHandler.getClass().getName();
                try {
                    String username = ((UsernamePasswordCredentials) credentials).getUsername();
                    ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
                    // reached the threshold--->warning
                    String loginFailTimesKey = LOGIN_FAIL_TIMES + username;
                    //loginFailTimesKey > 5,loginFailFlagKey才有值
                    String loginFailFlagKey = LOGIN_FAIL_FLAG + username;
                    String failFlag = valueOperations.get(loginFailFlagKey);
                    if (StringUtils.isNotEmpty(failFlag)) {
                        throw OutBindAuthenticationException.ERROR;
                    }
                    //!authenticationHandler.authenticate(credentials)去数据库校验用戶密碼
                    if (!authenticationHandler.authenticate(credentials)) {
                        //登录校验用户名密码失败
                        log.info("{} failed to authenticate {}", handlerName, credentials);
                        // redis operation 记录密码出错次数
                        if (valueOperations.get(loginFailTimesKey) == null) {
                            long milliSecondsLeftToday = 86400000 - DateUtils.getFragmentInMilliseconds(Calendar.getInstance(), Calendar.DATE);
                            //首次出错,记录为1
                            valueOperations.set(loginFailTimesKey, "1", milliSecondsLeftToday, TimeUnit.MILLISECONDS);
                        } else {
                            //非首次出错,次数+1
                            valueOperations.increment(loginFailTimesKey, 1);
                        }
                        //出错次数>5次 ,锁定30分钟
                        if (Long.valueOf(valueOperations.get(loginFailTimesKey)) >= 5) {
                            valueOperations.set(loginFailFlagKey, "1", 30, TimeUnit.MINUTES);
                        }
                    } else {
                        //登录成功
                        log.info("{} successfully authenticated {}", handlerName, credentials);
                        authenticatedClass = authenticationHandler;
                        authenticated = true;
                        //清除密碼錯誤次數緩存
                        redisTemplate.delete(loginFailTimesKey);
                        break;
                    }
                } catch (final Exception e) {
                    handleError(handlerName, credentials, e);
                }
            }
        }

casLoginView 这是前端页面,然后提交form表单数据到:AuthenticationViaFormAction,这里的submit方法是校验入口,然后再到QueryDatabaseAuthenticationHandler, 这个是从数据库拿值判断,然后再到AuthenticationManagerImpl 返回到这里,所以最终实现是在该类的authenticateAndObtainPrincipal方法里面,代码就在上面。

这里要提一下,我这个需求如果用户错5次,不允许其登录了,但是也要给个提示信息啊,所以这里我自定义了一个异常类OutBindAuthenticationException,如果用户错5次,

抛出该异常 throw OutBindAuthenticationException.ERROR;这里面的CODE,国际化支持文件messages_zh_CN.properties里面定义,是ascII码。OK就这样

原文地址:https://www.cnblogs.com/mmh760/p/11063762.html