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