前一篇文章介绍过CAS自定义用户信息, 这个用户信息实在登录成功后加到session中的, 今天来介绍一下CAS5.3怎么自定义登录校验逻辑
思路
有了前一篇文章的铺垫这一次自定义登录校验其实就很简单了, 因为想要把用户信息放入到session中是一定要在登陆的时候完成的,因此这次我们需要做的就是重新写一个登录校验的逻辑
重写AbstractPreAndPostProcessingAuthenticationHandler
前一篇文章中已经用到了这个抽象类, 这次要要做的还是改造它, 我们可以在之前的基础上来改造
/**
* 自定义登录拦截器
*
* @author zzt
* @version v1.0.0
* @date 2024/7/17
*/
@Setter
public class MyAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
private SysUserService sysUserService;
public MyAuthenticationHandler(String name,
ServicesManager servicesManager,
PrincipalFactory principalFactory,
Integer order) {
super(name, servicesManager, principalFactory, order);
}
@Override
protected AuthenticationHandlerExecutionResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
final UserPwdCredential originalUserPass = (UserPwdCredential) credential;
String username = originalUserPass.getUsername();
String password = originalUserPass.getPassword();
String phone = originalUserPass.getPhone();
String smsVerifyCode = originalUserPass.getSmsVerifyCode();
//查询用户信息
SysUser sysUser = sysUserService.getBaseMapper().selectOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUserName, username));
if (ObjectUtils.isNotEmpty(sysUser)) {
if (StringUtils.equals(username, sysUser.getUserName()) &&
StringUtils.equals(PasswordUtil.getPassword(password, sysUser.getSalt()), sysUser.getPassword())) {
if (Objects.equals(sysUser.getPhone(), phone) && StringUtils.isNotBlank(smsVerifyCode)) {
//仅判断用户是否输入了自己的手机号,需要自行修改
return createHandlerResult(credential,
this.principalFactory.createPrincipal(username), Collections.emptyList());
} else {
throw new RuntimeException("验证码错误");
}
} else {
throw new RuntimeException("用户名或密码错误");
}
} else {
throw new RuntimeException("用户不存在");
}
}
@Override
public boolean supports(Credential credential) {
return credential instanceof UserPwdCredential;
}
}
这里逻辑其实很简单, 就是验证完用户名密码之后还要验证手机号验证码, 我们常用到的双因子认证, 之前文章中有提到过下面这个方法,请继续往下看
Credential
这里是CAS默认登录逻辑的参数, 仅有用户名和密码, 在上一篇文章中也有提到过这里其实是一个策略模式, 因此我们只需要继承UsernamePasswordCredential类就可以了, 注意这里前端在给后端传参时一定要是这四个属性, 不然是无法进入到我们这个实现类的, 在这里我卡了挺长时间
继承后的实现
/**
* 自定义登录参数
*
* @author zzt
* @version v1.0.0
* @date 2024/7/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class UserPwdCredential extends UsernamePasswordCredential {
@Size(min = 1, message = "手机号不能为空!")
private String phone;
@Size(min = 1, message = "验证码不能为空!")
private String smsVerifyCode;
}
继承DefaultLoginWebflowConfigurer
/**
* 自定义登录
*
* @author zzt
* @version v1.0.0
* @date 2024/7/17
*/
public class CustomLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer {
public CustomLoginWebflowConfigurer(FlowBuilderServices flowBuilderServices, FlowDefinitionRegistry flowDefinitionRegistry, ApplicationContext applicationContext, CasConfigurationProperties casProperties) {
super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
}
@Override
protected void createRememberMeAuthnWebflowConfig(Flow flow) {
createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, UserPwdCredential.class);
final ViewState state = getState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, ViewState.class);
final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
cfg.addBinding(new BinderConfiguration.Binding("phone", null, true));
cfg.addBinding(new BinderConfiguration.Binding("smsVerifyCode", null, true));
}
}
我们还需要把要接收的这两个新参数添加到这里来
改前端
这里的前端页面用到的是webflow技术, 还是比较老的, 因此改起来比较麻烦,这里我们就参考原来的写法,给它新加参数即可
修改 resources/template/loginfrom.html
<section class="form-group form-grouptwo">
<!-- <label for="username" th:utext="#{screen.welcome.label.netid}">Username</label>-->
<div th:if="${openIdLocalId}">
<strong>
<span th:utext="${openIdLocalId}"/>
</strong>
<input type="hidden"
id="username"
name="username"
th:value="${openIdLocalId}"/>
</div>
<div th:unless="${openIdLocalId}" class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">
<img th:src="@{images/zhanghao.png}" alt="Username Icon"/>
</span>
</div>
<input class="form-control required form-place"
id="username"
size="25"
tabindex="1"
placeholder="请输入登录账号"
type="text"
th:disabled="${guaEnabled}"
th:field="*{username}"
th:accesskey="#{screen.welcome.label.netid.accesskey}"
autocomplete="off"/>
</div>
</section>
<section class="form-group form-grouptwo">
<!-- <label for="password" th:utext="#{screen.welcome.label.password}">Password</label>-->
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">
<!-- <img src="path/to/your/image.png" alt="Password Icon" width="20" height="20">-->
<img th:src="@{images/mima.png}" alt="Password Icon"/>
</span>
</div>
<input class="form-control required form-place"
type="password"
id="password"
size="25"
tabindex="2"
placeholder="请输入登录密码"
th:accesskey="#{screen.welcome.label.password.accesskey}"
th:field="*{password}"
autocomplete="off"/>
<span id="capslock-on" style="display:none;">
<p>
<i class="fa fa-exclamation-circle"></i>
<span th:utext="#{screen.capslock.on}"/>
</p>
</span>
</div>
</section>
<!-- 手机-->
<section class="form-group form-grouptwo">
<!-- <label for="password" th:utext="#{screen.welcome.label.password}">Password</label>-->
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">
<!-- <img src="path/to/your/image.png" alt="Password Icon" width="20" height="20">-->
<img th:src="@{images/shoujihao.png}" alt="shoujihao Icon"/>
</span>
</div>
<input class="form-control required form-place"
type="text"
id="phone"
th:field="*{phone}"
size="25"
tabindex="3"
placeholder="请输入手机号码"
autocomplete="off"/>
<span id="capslock-on" style="display:none;">
<p>
<i class="fa fa-exclamation-circle"></i>
<span th:utext="#{screen.capslock.on}"/>
</p>
</span>
</div>
</section>
<!--验证码-->
<section class="form-group form-grouptwo">
<!-- <label for="password" th:utext="#{screen.welcome.label.password}">Password</label>-->
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">
<!-- <img src="path/to/your/image.png" alt="Password Icon" width="20" height="20">-->
<img th:src="@{images/yanzhengma.png}" alt="smsVerifyCode Icon"/>
</span>
</div>
<input class="form-control required form-place form-absolute"
type="text"
id="smsVerifyCode"
th:field="*{smsVerifyCode}"
size="25"
tabindex="4"
placeholder="请输入验证码"
autocomplete="off"/>
<button class="form-sendbutton" id="sendCodeBtn">发送验证码</button>
<span id="countdown" class="countdown"></span>
<span id="capslock-on" style="display:none;">
<p>
<i class="fa fa-exclamation-circle"></i>
<span th:utext="#{screen.capslock.on}"/>
</p>
</span>
</div>
</section>