apereo cas

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

2018-07-01  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读