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的意思就是在多系统的环境中,登录单方系统,就可以在不用再次登录的情况下访问相关受信任的系统. ...
随机推荐
- OpenCV.资料(20190717)
1.opencv将图片转换为视频 - zeng_haoyu的博客 - CSDN博客.html(https://blog.csdn.net/hy13684802853/article/details/8 ...
- Vue Cli3.0 使用jquery
参考链接:https://blog.csdn.net/ai520587/article/details/84098601
- python xlrd模块
一.什么是xlrd模块? Python操作excel主要用到xlrd和xlwt这两个库,即xlrd是读excel,xlwt是写excel的库. 二.使用介绍 1.常用单元格中的数据类型 类型 含义 e ...
- ubuntu 拨号上网
如果没有安装的用户,可以使用 sudo apt-get install pppoe pppoeconf 然后配置上网 sudo pppoeconf 最后,使用 sudo pon dsl-provide ...
- LC 387. First Unique Character in a String
题目描述 Given a string, find the first non-repeating character in it and return it's index. If it doesn ...
- PostgreSQL练习2
列转行CREATE TABLE sdb.t_col_row(id int, c1 varchar(10), c2 varchar(10), c3 varchar(10)) INSERT INTO sd ...
- npm—入门指导
npm npm是什么? NPM(node package manager),通常称为node包管理器.顾名思义,它的主要功能就是管理node包,包括:安装.卸载.更新.查看.搜索.发布等. npm的背 ...
- S03_CH01_AXI_DMA_LOOP 环路测试
S03_CH01_AXI_DMA_LOOP 环路测试 1.1概述 本课程是本季课程里面最简单,也是后面DMA课程的基础,读者务必认真先阅读和学习. 本课程的设计原理分析. 本课程是设计一个最基本的DM ...
- 01 Java 内存分配全面浅析
http://blog.csdn.net/shimiso/article/details/8595564 Java 内存分配全面浅析 本文将由浅入深详细介绍Java内存分配的原理,以帮助新手更轻松的 ...
- SIP中第三方呼叫控制(3PCC)建立流程
1.引言 在传统的电话网环境中,第三方呼叫控制允许一个实体(这里称为控制器- controller) 建立并管理另外的两方或多方之间的通信关系,而其本身并不参与通信. 第三方呼叫控制经常用于运营商业务 ...