cas多方式登录相关知识点的总结
知识点:
cas多表单登录(在用户名,密码的基础上,增加短信验证码登录)
自定义认证策略
自定义字段添加为空校验的错误信息
Controller层接口的调用
一:场景
项目涉及到的业务是,在原cas用户名,密码登录的基础上,增加短信验证码登录和ca登录,在同事代码的基础上,加以修改添加功能,在这个过程中遇到了一些问题,下面针对验证码登录实现的思路和知识点加以总结
二:cas短信验证码实现涉及到的知识点
(1)实现思路
关于cas的概念,和搭建的一系列知识点的介绍,可参考博客 https://blog.csdn.net/Anumbrella (里面有一系列的教程)
由于在登录界面,提交的form表单对象,在源码里定义了为credential对象,自己打算再添加一个类似credential对象作为短信验证码登录的对象,发现代码不太好改,于是在credential实体类中加了一个 loginType (登录类型字段)作为区分不同表单登录的标识,然后结合自定义认证策略,给对象再加上phone(电话号码)和noteCode(短信验证码)两个字段,做为第二个form的属性,最后在自定义策略继承AbstractPreAndPostProcessingAuthenticationHandler抽象类的CustomerHandlerAuthentication对象的doAuthentication方法中,根据loginType判断登录方式,去不同字段,做逻辑处理。
(2)自定义认证策略(提交的信息不只用户名密码时)
当我们登录时,不仅需要验证cas框架自带的用户名密码验证,还需要验证其他邮箱,图片验证码(同一表单)、手机号,短信验证码(第二表单)时,需要我们自定义认证策略,自定义Cas的web认证流程。
a.引入依赖
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-authentication-api</artifactId>
<version>${cas.version}</version>
</dependency> <!-- Custom Configuration -->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-configuration-api</artifactId>
<version>${cas.version}</version>
</dependency>
b.新建CustomerHandlerAuthentication继承AbstractPreAndPostProcessingAuthenticationHandler
//自定义策略
public class CustomerHandlerAuthentication extends AbstractPreAndPostProcessingAuthenticationHandler {
private SnowFlakeWorker worker = new SnowFlakeWorker(0);
@Resource(name = "casDataSource")
private DataSource dataSource; public CustomerHandlerAuthentication(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
super(name, servicesManager, principalFactory, order);
}
@Override
protected AuthenticationHandlerExecutionResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
try {
CustomCredential logincredential = (CustomCredential) credential;
//获取登录类型标识,选择登录验证方式
String logintype= logincredential.getLogintype();
String username = logincredential.getUsername();
String password = logincredential.getPassword();
String phone = logincredential.getPhone();
String nodeCode = logincredential.getNoteCode();
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//1.用户密码登录
if(logintype.equals("1")){
String sql = "SELECT * FROM base_user WHERE login_code = ? and logic_delete = 0";
BaseUser user = (BaseUser) jdbcTemplate.queryForObject(sql, new Object[]{username}, new BeanPropertyRowMapper(BaseUser.class));
if (user == null) {
throw new AccountException("用户不存在!");
}
if (!user.getLoginPassword().equals(SecureUtil.md5(String.valueOf(password)))) {
throw new FailedLoginException("用户名密码不正确!");
} else {
//将登录类型一个全局变量里
CasVariate.loginType=logintype; //可自定义返回给客户端的多个属性信息
saveLoginLog(user.getUserId(), jdbcTemplate);
final List<MessageDescriptor> list = new ArrayList<>();
return createHandlerResult(logincredential,
this.principalFactory.createPrincipal(username), list);
}
//2.短信验证码登录
}else if(logintype.equals("2")){
String sql = "SELECT * FROM base_user WHERE mobliephone = ? and logic_delete = 0";
BaseUser user = (BaseUser) jdbcTemplate.queryForObject(sql, new Object[]{phone}, new BeanPropertyRowMapper(BaseUser.class));
if (user == null) {
throw new AccountException("改手机号尚未注册!");
}
//if (!user.getNoteCode().equals(nodeCode)) {
if (!CasVariate.nodeCodeMap.get(phone).equals(nodeCode)) {
throw new FailedLoginException("验证码填写错误!");
} else { //将登录类型一个全局变量里
CasVariate.loginType=logintype;
//可自定义返回给客户端的多个属性信息
saveLoginLog(user.getUserId(), jdbcTemplate);
final List<MessageDescriptor> list = new ArrayList<>();
return createHandlerResult(logincredential,
this.principalFactory.createPrincipal(user.getLoginCode()), list);
}
}
//3.ca登录 } catch (Exception e) {
System.out.println(e.getMessage());
throw new AccountException("登录失败!");
}
return null;
}
@Override
public boolean supports(Credential credential) {
//判断传递过来的Credential 是否是自己能处理的类型
//return credential instanceof UsernamePasswordCredential;
return credential instanceof CustomCredential;//短信验证实体类
}
private void saveLoginLog(Long userId, JdbcTemplate jdbcTemplate) {
jdbcTemplate.update("INSERT INTO dc_j_login_log (log_id,user_id,login_time) VALUES(?, ?, ?)",
ps -> {
ps.setLong(1, worker.nextId());
ps.setLong(2, userId);
ps.setString(3, DateUtil.formatDateTime(new Date()));
});
}
}
c.把CustomAuthenticationConfiguration中myAuthenticationHandle中改为
@Configuration("CustomAuthenticationConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer, CasWebflowExecutionPlanConfigurer {
@Bean(name = "casDataSource")
@Qualifier("casDataSource")
@ConfigurationProperties(prefix="spring.datasource")
public DataSource getCasDataSource(){
return DataSourceBuilder.create().build();
}
@Autowired
private CasConfigurationProperties casProperties;
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
@Bean
public AuthenticationHandler myAuthenticationHandler() {
// 参数: name, servicesManager, principalFactory, order
// 定义为优先使用它进行认证
return new CustomerHandlerAuthentication(CustomerHandlerAuthentication.class.getName(),
servicesManager, new DefaultPrincipalFactory(), 1);
} @Override
public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(myAuthenticationHandler());
}
}
(3)自定义字段添加为空校验的错误信息(phone,noteCode为空校验错误信息)
在表单提交前在submit中的return 后面的方法中添加校验是可以的(saveLoginType())

以一种方式是更改webflow流程,先进行验证后,再经过我们自定义校验
a.在CustomWebflowConfigurer中更改bindCredential方法
public class CustomWebflowConfigurer extends AbstractCasWebflowConfigurer {
public CustomWebflowConfigurer(FlowBuilderServices flowBuilderServices, FlowDefinitionRegistry loginFlowDefinitionRegistry, ApplicationContext applicationContext, CasConfigurationProperties casProperties) {
super(flowBuilderServices, loginFlowDefinitionRegistry, applicationContext, casProperties);
}
@Override
protected void doInitialize() {
final Flow flow = super.getLoginFlow();
bindCredential(flow);
}
/**
* 绑定自定义的Credential信息
* @param flow
*/
protected void bindCredential(Flow flow) {
// 重写绑定自定义credential
createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, CustomCredential.class);
// 登录页绑定新参数
final ViewState state = (ViewState) flow.getState(CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM);
final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
// 由于用户名以及密码已经绑定,所以只需对新加系统参数绑定即可
// 字段名,转换器,是否必须字段
cfg.addBinding(new BinderConfiguration.Binding("logintype", null, true));
cfg.addBinding(new BinderConfiguration.Binding("phone", null, false));
cfg.addBinding(new BinderConfiguration.Binding("noteCode", null, false));
final ActionState actionState = (ActionState) flow.getState(CasWebflowConstants.STATE_ID_REAL_SUBMIT);
final List<Action> currentActions = new ArrayList<>();
actionState.getActionList().forEach(currentActions::add);
currentActions.forEach(a -> actionState.getActionList().remove(a));
actionState.getActionList().add(createEvaluateAction("validateLoginAction"));
currentActions.forEach(a -> actionState.getActionList().add(a));
actionState.getTransitionSet().add(createTransition("phoneError", CasWebflowConstants.STATE_ID_INIT_LOGIN_FORM));
actionState.getTransitionSet().add(createTransition("noteCodeError", CasWebflowConstants.STATE_ID_INIT_LOGIN_FORM));
}
}
将phone和noteCode字段必填改为false,添加Webflow的流程,将原来的action备份一下,删掉,再将需要的action添加进去,同时将备份的action还原
b.新建ValidateLoginAction类,添加表单信息的验证
public class ValidateLoginAction extends AbstractAction {
private static final String PHONE_CODE = "phoneError";
private static final String NOTE__CODE = "noteCodeError";
@Override
protected Event doExecute(RequestContext requestContext) throws Exception {
CustomCredential credential = (CustomCredential) WebUtils.getCredential(requestContext);
if (credential instanceof CustomCredential) {
String logintype=credential.getLogintype();
String phone = credential.getPhone();
String noteCode = credential.getNoteCode();
//字段为空提交时
//将登录类型一个全局变量里
CasVariate.loginType=logintype;
//短信登录
if(logintype.equals("2")) {
//电话号码为空校验
if (phone.equals("") || phone == null) {
return getError(requestContext, PHONE_CODE);
}
//电话号码为空校验
if (noteCode.equals("") || noteCode == null) {
return getError(requestContext, NOTE__CODE);
}
}
}
return null;
}
/**
* 跳转到错误页
* @param requestContext
* @return
*/
private Event getError(final RequestContext requestContext, String CODE) {
final MessageContext messageContext = requestContext.getMessageContext();
messageContext.addMessage(new MessageBuilder().error().code(CODE).build());
return getEventFactorySupport().event(this, CODE);
}
}
c.将ValidateLoginAction类注入到CustomerAuthWebflowConfiguration中
//注入ValidateLoginAction
@Bean
@RefreshScope
@ConditionalOnMissingBean(name = "validateLoginAction")
public Action validateLoginAction() {
ValidateLoginAction validateCaptchaAction = new ValidateLoginAction();
return validateCaptchaAction;
} d.将messages_zh_CN.properties拷贝一份,到resource下添加,(乱码时,可以在idea中设置一下)
#自定义为空异常
phoneError=手机号不能为空
noteCodeError=短信验证码不能为空
e.效果 (4)Controller层接口的调用(自定义访问控制器)
controller层
@Controller
@RequestMapping(value = "/note")
public class CustomController { private static final Logger LOGGER = LoggerFactory.getLogger(CustomController.class); @Resource(name = "casDataSource")
private DataSource dataSource; /**
* 保存手机验证码
* @return
*/
@RequestMapping(value = "/saveCode", method = { RequestMethod.GET, RequestMethod.POST })
@ResponseBody
public void saveNodeCode(String phone,String noteCode) {
//保存手机验证码到map中
CasVariate.nodeCodeMap.put(phone,noteCode);
}
}
新建CustomControllerConfiguration类,将CustomController注入bean到spring容器
//自定义控制器
@Configuration("CustomControllerConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomControllerConfiguration { // 注册bean到spring容器
@Bean
@ConditionalOnMissingBean(name="CustomController")
public CustomController customController(){
return new CustomController();
}
}
cas多方式登录相关知识点的总结的更多相关文章
- CAS单点登录相关配置
一.CAS单点登录服务端的部署 部署 把CAS所对应的war包部署到tomcat中 4.品优购资源V1.3\配套软件\配套软件\CAS\cas.war 配置 更改tomcat的端口号 <Conn ...
- SSO之CAS单点登录实例演示
本文目录: 一.概述 二.演示环境 三.JDK安装配置 四.安全证书配置 五.部署CAS-Server相关的Tomcat 六.部署CAS-Client相关的Tomcat 七. 测试验证SSO 一.概述 ...
- SSO之CAS单点登录详细搭建教程
本教程是我个人编写,花费几个小时的时间,给需要学习的人员学习使用,希望能帮助到你们. [环境说明]:本文演示过程在同一个机器上的(也可以在三台实体机器或者三个的虚拟机上),环境如下: windows7 ...
- CAS单点登录(SSO)完整教程
转:http://blog.csdn.net/frinder/article/details/7969925 CAS单点登录(SSO)完整教程(2012-02-01更新) 一.教程说明 前言 教程目的 ...
- CAS单点登录原理以及debug跟踪登录流程
CAS 原理和协议 基础模式 基础模式 SSO 访问流程主要有以下步骤: 1. 访问服务: SSO 客户端发送请求访问应用系统提供的服务资源. 2. 定向认证: SSO 客户端会重定向用户请求到 SS ...
- CAS单点登录(SSO)服务端的部署和配置---连接MySQL进行身份认证
一.修改系统host,加入 127.0.0.1 server.test.com127.0.0.1 client1.test.com127.0.0.1 client2.test.com 二.安装grad ...
- CAS单点登录服务器搭建
关于cas单点登录的原理及介绍这里不做说明了,直接开始: 1.war包下载 去官网(https://www.apereo.org/projects/cas/download-cas)下载cas_ser ...
- cas单点登录系统:客户端(client)详细配置
最近一直在研究cas登录中心这一块的应用,分享一下记录的一些笔记和心得.后面会把cas-server端的配置和重构,另外还有这几天再搞nginx+cas的https反向代理配置,以及cas的证书相关的 ...
- CAS单点登录原理简单介绍
1. SSO简介 1.1 单点登录定义 单点登录(Single sign on),英文名称缩写SSO,SSO的意思就是在多系统的环境中,登录单方系统,就可以在不用再次登录的情况下访问相关受信任的系统. ...
随机推荐
- 外带IP 防火墙限制导致 IP不通
案例: 业务报障,一台设备配了20个IP,跳板机测试都通,但从外边访问,发现部分IP通,部分不通. 排雷: 1. 从跳板机测试都通,说明所有IP 本身应该都没问题的,都可以用,2. 从其他设备测试,部 ...
- 如何在ubuntu下重建被grub覆盖的win10引导区?
如何在ubuntu下重建被grub覆盖的win10引导区? 1.修改grub配置文件: sudo vi /etc/default/grub 2.设置:GRUB_DEFAULT = 2 3.更新配置文件 ...
- 如何通过Exchange2010 OWA更改过期密码
很多Exchange 2003管理员都通过IISADMPWD虚拟目录为员工提供用户密码修改功能,这大大方便了移动用户和非加入域用户在密码到期时的更改操作.您也许已经注意到:Windows Server ...
- 【Matlab开发】函数bsxfun的使用
[Matlab开发]函数bsxfun的使用 标签:[Matlab开发] 版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/lg1259156776/. 说明:当我们 ...
- 理解clientX、clientY、offsetLeft、event.offsetTop、offsetWidth、offsetHeight、clientWidth、clientHeight、scrollTop、scrollHeight
一.clientX和clientY 事件发生时,鼠标距离浏览器的可视区域的X.Y轴的位置,不包含滚动条的区域的部分.就算是页面进行了滚动,鼠标的坐标值还是参考可视区域的. 二.offsetLeft和o ...
- POJ 1734:Sightseeing trip
Sightseeing trip Time Limit: 1000MS Memory Limit: 65536K Total Submissions: Accepted: Special Judge ...
- [Agc029E]Wandering TKHS_树形dp_树上差分
Wandering TKHS 题目链接:https://atcoder.jp/contests/agc029/tasks/agc029_e 数据范围:略. 题解: 好神啊 Orz司队 https:// ...
- Design Compressed String Iterator
Design and implement a data structure for a compressed string iterator. It should support the follow ...
- Photon Server初识(五) --- 客户端连接服务端
准备:Unity开开发IDE. 一.新建Unity3D项目 导入包,在资源下新建目录 Plugins .在之前解压的SDK目录 lib中找到 Photon3Unity3D.dll,拖到新建的目前下 二 ...
- python flask 如何读取数据库数据并返回到html
app.py from flask import Flask from flask import render_template from flask_bootstrap import Bootstr ...