美文网首页apereo cas
apereo cas记录用户登陆日志到数据库中

apereo cas记录用户登陆日志到数据库中

作者: 说你还是说我 | 来源:发表于2018-07-01 18:37 被阅读369次

    一段时间没写文章了,大早上起来就打开电脑。寻思着写点什么,想着写一点单点登陆的文章。笔者以前遇到需要在单点登陆的时候获取用户的登陆ip等一些数据,一直没花时间去看这方面的知识,今天就来研究一下。

    本篇基于spring securiry 登录 添加验证码过滤器来写的,原因是要用到dataSource,所以我们就必须自定义一个基于database的认证方式,这样才能获取到数据源。也才能把用户的登陆、登出日志记录到数据库中。
    下面是笔者的思路:
    基于Filter来实现登陆拦截,然后获取用户的登陆数据。保存到数据库中。

    一开始用的是servlet来做,但是在访问cas登陆的页面,莫名其妙的出现405错误,可能是cas内部做了特别的处理了。后面就改用了filer。

    步骤:

    1.自定义一个filter
    需要自己过滤掉登陆登出的url,来区分登陆、登出操作。
    2.在自定义dataSource的实现类中,需要把dataSource返回出来。因为cas内部封装的dataSource为protected,我们其它的类获取不到。
    3.注册filter

    以下是部分代码片段

    返回数据库连接接

    public class MyAuthenticationHandler extends CustAbstractJdbcUsernamePasswordAuthenticationHandler {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(MyAuthenticationHandler.class);
    
        private final String sql;
        private final String fieldPassword;
        private final String fieldExpired;
        private final String fieldDisabled;
        private final Map<String, Collection<String>> principalAttributeMap;
        
      /*
       * 返回一个jdbcTemplate,这里返回的数据,只要能保证获取到connection就可以了
       */
        public JdbcTemplate getJdbcTemplate() {
            return super.getJdbcTemplate();
        }
    
        public MyAuthenticationHandler(final String name, final ServicesManager servicesManager,
                                       final PrincipalFactory principalFactory,
                                       final Integer order, final DataSource dataSource, final String sql,
                                       final String fieldPassword, final String fieldExpired, final String fieldDisabled,
                                       final Map<String, Collection<String>> attributes) {
            super(name, servicesManager, principalFactory, order, dataSource);
            this.sql = sql;
            this.fieldPassword = fieldPassword;
            this.fieldExpired = fieldExpired;
            this.fieldDisabled = fieldDisabled;
            this.principalAttributeMap = attributes;
        }
    
    ....
    }
    

    自定义filter,这里手动处理了数据库insert语句事务提交。

    public class MyFilter implements Filter {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(MyFilter.class);
    
        private String loginPrefix;
        private String logoutPrefix;
    
        private enum OperationType{
            LOGIN("登陆","login"),
            LOGOUT("登出","logout");
    
            private String operationType;
            private String operationCode;
    
            OperationType(String operationType, String operationCode) {
                this.operationType = operationType;
                this.operationCode = operationCode;
            }
    
            public String getOperationType() {
                return operationType;
            }
    
            public String getOperationCode() {
                return operationCode;
            }
        }
        private final JdbcTemplate jdbcTemplate;
    
        private final String sql = "INSERT INTO USER_LOGIN_RECORD (USER_ACCT,IP_ADDRESS,OPERATION_DATE,OPERATION_TYPE) VALUES (?,?,?,?)";
    
        public MyFilter(String loginPrefix, String logoutPrefix, JdbcTemplate jdbcTemplate) {
            this.loginPrefix = loginPrefix;
            this.logoutPrefix = logoutPrefix;
            this.jdbcTemplate = jdbcTemplate;
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            LOGGER.info("MyFilter init");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            String remoteAddr = httpServletRequest.getRemoteAddr();
            String url = httpServletRequest.getRequestURL().toString();
            String userAcct = httpServletRequest.getParameter("username");
            HttpSession session = httpServletRequest.getSession();
            if(url.contains(loginPrefix)){
                if(!StringUtils.isEmpty(userAcct)){
                    this.insertUserRecord(remoteAddr, userAcct,OperationType.LOGIN);
    
                    session.setAttribute("userAcct",userAcct);
                }
            } else if(url.contains(logoutPrefix)){
                userAcct = (String) session.getAttribute("userAcct");
                if(!StringUtils.isEmpty(userAcct)) {
                    this.insertUserRecord(remoteAddr, userAcct, OperationType.LOGOUT);
                }
            }
    
            filterChain.doFilter(servletRequest,servletResponse);
    
        }
    
        private void insertUserRecord(String remoteAddr, String userAcct,OperationType operationType) {
            Connection conn = null;
            PreparedStatement pstmt = null;
            try {
                conn = this.jdbcTemplate.getDataSource().getConnection();
                conn.setAutoCommit(false);
                pstmt = conn.prepareStatement(sql);
                pstmt.setString(1,userAcct);
                pstmt.setString(2,remoteAddr);
                pstmt.setObject(3, new Date());
                pstmt.setString(4, operationType.getOperationCode());
                int count = pstmt.executeUpdate();
                pstmt.close();
                conn.commit();
            } catch (SQLException e) {
                LOGGER.error("insertUserRecord error",e);
                try {
                    if(null != conn) {
                        conn.rollback();
                        conn.setAutoCommit(true);
                    }
    
                } catch (SQLException e1) {
                    LOGGER.error("insertUserRecord rollback error",e);
                }
            } finally {
                try {
                    if(null != conn) {
                        conn.close();
                    }
                } catch (SQLException e) {
                    LOGGER.error("insertUserRecord close error",e);
                }
            }
    
        }
    
        @Override
        public void destroy() {
    
        }
    
    }
    

    注册filter

    
        @Bean
        public MyAuthenticationHandler myAuthenticationHandler(){
            final JdbcAuthenticationProperties jdbc = casProperties.getAuthn().getJdbc();
            QueryJdbcAuthenticationProperties b = jdbc.getQuery().get(0);
            final Multimap<String, String> attributes = CoreAuthenticationUtils.transformPrincipalAttributesListIntoMultiMap(b.getPrincipalAttributeList());
            final MyAuthenticationHandler h = new MyAuthenticationHandler(b.getName()+"_amazing", servicesManager,
                    new DefaultPrincipalFactory(), b.getOrder(),
                    JpaBeans.newDataSource(b), b.getSql(), b.getFieldPassword(),
                    b.getFieldExpired(), b.getFieldDisabled(),
                    CollectionUtils.wrap(attributes));
    
            h.setPasswordEncoder(PasswordEncoderUtils.newPasswordEncoder(b.getPasswordEncoder()));
            h.setPrincipalNameTransformer(PrincipalNameTransformerUtils.newPrincipalNameTransformer(b.getPrincipalTransformation()));
            if (queryPasswordPolicyConfiguration != null) {
                h.setPasswordPolicyConfiguration(queryPasswordPolicyConfiguration);
            }
    
            h.setPrincipalNameTransformer(PrincipalNameTransformerUtils.newPrincipalNameTransformer(b.getPrincipalTransformation()));
    
            if (StringUtils.isNotBlank(b.getCredentialCriteria())) {
                h.setCredentialSelectionPredicate(CoreAuthenticationUtils.newCredentialSelectionPredicate(b.getCredentialCriteria()));
            }
            LOGGER.debug("Created authentication handler [{}] to handle database url at [{}]", h.getName(), b.getUrl());
            return h;
        }
    
        @Bean
        @ConditionalOnBean(AuthenticationHandler.class)
        public FilterRegistrationBean registration() {
    
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(new MyFilter(loginPrefix,logoutPrefix,this.myAuthenticationHandler().getJdbcTemplate()));
            registration.addUrlPatterns("/*");
            registration.addInitParameter("paramName", "paramValue");
            registration.setName("MyFiltersss");
            registration.setOrder(1);
            return registration;
        }
    
    

    然后就可以打包部署到服务器上了。出现未识别的服务器名的情况,可以参考下https://my.oschina.net/heguangdong/blog/13678
    表结构脚本放在sql文件夹下
    登陆用户:amazing
    密码:123321

    码云地址:https://gitee.com/longguiyunjosh/apereo-cas/tree/amzing

    相关文章

      网友评论

      本文标题:apereo cas记录用户登陆日志到数据库中

      本文链接:https://www.haomeiwen.com/subject/htkhuftx.html