CAS单点登录:配置记住我,添加验证码(五)
1.配置RememberMe
1.1.修改application.properties
## # 记住我 # cas.ticket.tgt.rememberMe.enabled=true cas.ticket.tgt.rememberMe.timeToKillInSeconds=3600
1.2.修改登录界面
<div class="form-group" th:if="${rememberMeAuthenticationEnabled}">
<input type="checkbox" name="rememberMe" id="rememberMe" value="true" tabindex="5"/>
<label for="rememberMe" th:text="#{screen.rememberme.checkbox.title}">Remember Me</label>
</div>
1.3.测试流程
第一步:首先 不选择记住我登录 然后退出浏览器。
第二步:打开浏览器,再次访问服务 发现需要登录。
第三步:选择 记住我登录,然后退出浏览器。
第四步:打开浏览器,访问服务,直接就是登录成功状态。(前提是退出浏览器前不要登出)
2.添加验证码
从页面登录页面上我们可以知道,登陆的用户名和密码信息绑定到了credential这个对象上的。
如果开启了RememberMe的功能就使用RememberMeUsernamePasswordCredential。
如果没有就使用UsernamePasswordCredential了,这里我们使用RememberMeUsernamePasswordCredential。
2.1.添加依赖
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-authentication</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-authentication-api</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-webflow</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-webflow-api</artifactId>
<version>${cas.version}</version>
</dependency>
这里可能会在后续继承DefaultLoginWebflowConfigurer时无法导入依赖
虽然cas-server-core-webflow中有那个类,但是无法导入,所以单独引入cas-server-core-webflow-api这个依赖
2.2.重写credential
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apereo.cas.authentication.RememberMeUsernamePasswordCredential;public class RememberMeUsernamePasswordCaptchaCredential extends RememberMeUsernamePasswordCredential {private String captcha;
public String getCaptcha() {
return captcha;
}
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.appendSuper(super.hashCode())
.append(this.captcha)
.toHashCode();
}
}
2.3.新建DefaultCaptchaWebflowConfigurer修改之前默认的Credential
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConstants;
import org.apereo.cas.web.flow.configurer.DefaultLoginWebflowConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
public class DefaultCaptchaWebflowConfigurer extends DefaultLoginWebflowConfigurer {
public DefaultCaptchaWebflowConfigurer(FlowBuilderServices flowBuilderServices, FlowDefinitionRegistry flowDefinitionRegistry, ApplicationContext applicationContext, CasConfigurationProperties casProperties) {
super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
}
@Override
protected void createRememberMeAuthnWebflowConfig(Flow flow) {
if (casProperties.getTicket().getTgt().getRememberMe().isEnabled()) {
createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, RememberMeUsernamePasswordCaptchaCredential.class);
final ViewState state = getState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, ViewState.class);
final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
cfg.addBinding(new BinderConfiguration.Binding("rememberMe", null, false));
cfg.addBinding(new BinderConfiguration.Binding("captcha", null, true));
} else {
createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, UsernamePasswordCredential.class);
final ViewState state = getState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, ViewState.class);
final BinderConfiguration cfg = this.getViewStateBinderConfiguration(state);
cfg.addBinding(new BinderConfiguration.Binding("captcha", null, true));
}
}
}
2.4.创建表单处理器
import com.fdzang.cas.service.framework.ApiResult;
import com.fdzang.cas.service.service.UserService;
import com.fdzang.cas.service.util.Constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.services.ServicesManager;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.security.auth.login.FailedLoginException;
import javax.servlet.http.HttpServletRequest;
import java.security.GeneralSecurityException;
@Slf4j
public class RememberMeUsernamePasswordCaptchaAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
private UserService userService;
public RememberMeUsernamePasswordCaptchaAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
super(name, servicesManager, principalFactory, order);
}
@Override
protected AuthenticationHandlerExecutionResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
RememberMeUsernamePasswordCaptchaCredential captchaCredential = (RememberMeUsernamePasswordCaptchaCredential) credential;
String requestCaptcha = captchaCredential.getCaptcha();
String username = captchaCredential.getUsername();
String password = captchaCredential.getPassword();
// 校验验证码
Object attribute = request.getSession().getAttribute(Constant.CAPTCHA_SESSION_KEY);
String realCaptcha = attribute == null ? null : attribute.toString();
if(StringUtils.isBlank(requestCaptcha) || !requestCaptcha.equalsIgnoreCase(realCaptcha)){
throw new FailedLoginException("验证码错误");
}
// 获取请求来源URL
String referer = request.getHeader("referer");
if(referer.indexOf("service=")>0){
referer = referer.substring(referer.indexOf("service=")+8);
referer.replace("%3A",":");
referer.replace("%2F","/");
}
RegisteredService service = findByServiceId(referer);
if (service != null){
throw new FailedLoginException("未查询到Service错误");
}
String appCode = service.getName();
// 登录校验
ApiResult result = userService.userLogin(username,password,appCode);
if(!result.getCode().equals(0L)){
throw new FailedLoginException(result.getMsg());
}
return createHandlerResult(credential, this.principalFactory.createPrincipal(username));
}
@Override
public boolean supports(Credential credential) {
return credential instanceof RememberMeUsernamePasswordCaptchaCredential;
}
public RegisteredService findByServiceId(String serviceId){
RegisteredService service = null;
try {
service = servicesManager.findServiceBy(serviceId);
} catch (Exception e) {
log.error(e.getMessage());
}
return service;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
这里我根据自己的需求做了特定的登录校验,仅做参考。
2.5.配置DefaultCaptchaWebflowConfigurer
import com.fdzang.cas.service.captcha.DefaultCaptchaWebflowConfigurer;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.apereo.cas.web.flow.config.CasWebflowContextConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
@Configuration("captchaWebflowConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
@AutoConfigureBefore(value = CasWebflowContextConfiguration.class)
public class CaptchaWebflowConfiguration {
@Autowired
@Qualifier("loginFlowRegistry")
private FlowDefinitionRegistry loginFlowRegistry;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private CasConfigurationProperties casProperties;
@Autowired
@Qualifier("builder")
private FlowBuilderServices builder;
@Bean("defaultLoginWebflowConfigurer")
public CasWebflowConfigurer defaultLoginWebflowConfigurer() {
DefaultCaptchaWebflowConfigurer c = new DefaultCaptchaWebflowConfigurer(builder, loginFlowRegistry, applicationContext, casProperties);
c.initialize();
return c;
}
}
2.6.配置表单处理器
import com.fdzang.cas.service.captcha.RememberMeUsernamePasswordCaptchaAuthenticationHandler;
import com.fdzang.cas.service.service.UserService;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.services.ServicesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration("rememberMeConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class RememberMeCaptchaConfiguration implements AuthenticationEventExecutionPlanConfigurer {
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
@Autowired
private UserService userService;
@Bean
public AuthenticationHandler rememberMeUsernamePasswordCaptchaAuthenticationHandler() {
RememberMeUsernamePasswordCaptchaAuthenticationHandler handler = new RememberMeUsernamePasswordCaptchaAuthenticationHandler(
RememberMeUsernamePasswordCaptchaAuthenticationHandler.class.getSimpleName(),
servicesManager,
new DefaultPrincipalFactory(),
9);
handler.setUserService(userService);
return handler;
}
@Override
public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(rememberMeUsernamePasswordCaptchaAuthenticationHandler());
}
}
2.7.加载配置类,spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.apereo.cas.config.CasEmbeddedContainerTomcatConfiguration,\ org.apereo.cas.config.CasEmbeddedContainerTomcatFiltersConfiguration,\ com.fdzang.cas.service.config.SpringConfig,\ com.fdzang.cas.service.config.RememberMeCaptchaConfiguration,\ com.fdzang.cas.service.config.CaptchaWebflowConfiguration
2.8.验证码生成工具类
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class CaptchaUtil {
// 随机产生的字符串
private static final String RANDOM_STRS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String FONT_NAME = "Fixedsys";
private static final int FONT_SIZE = 18;
private Random random = new Random();
private int width = 80;// 图片宽
private int height = 25;// 图片高
private int lineNum = 50;// 干扰线数量
private int strNum = 4;// 随机产生字符数量
/**
* 生成随机图片
*/
public BufferedImage genRandomCodeImage(StringBuffer randomCode) {
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
g.setColor(getRandColor(110, 120));
for (int i = 0; i <= lineNum; i++) {
drowLine(g);
}
// 绘制随机字符
g.setFont(new Font(FONT_NAME, Font.ROMAN_BASELINE, FONT_SIZE));
for (int i = 1; i <= strNum; i++) {
randomCode.append(drowString(g, i));
}
g.dispose();
return image;
}
/**
* 给定范围获得随机颜色
*/
private Color getRandColor(int fc, int bc) {
if (fc > 255){
fc = 255;
}
if (bc > 255){
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
/**
* 绘制字符串
*/
private String drowString(Graphics g, int i) {
g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
.nextInt(121)));
String rand = String.valueOf(getRandomString(random.nextInt(RANDOM_STRS.length())));
g.translate(random.nextInt(3), random.nextInt(3));
g.drawString(rand, 13 * i, 16);
return rand;
}
/**
* 绘制干扰线
*/
private void drowLine(Graphics g) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int x0 = random.nextInt(16);
int y0 = random.nextInt(16);
g.drawLine(x, y, x + x0, y + y0);
}
/**
* 获取随机的字符
*/
private String getRandomString(int num) {
return String.valueOf(RANDOM_STRS.charAt(num));
}
}
2.9.验证码控制层
import com.fdzang.cas.service.util.CaptchaUtil;
import com.fdzang.cas.service.util.Constant;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
@RestController
public class CaptchaController {
@GetMapping("/captcha.jpg")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("image/jpeg");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expire", 0);
try {
HttpSession session = request.getSession();
CaptchaUtil tool = new CaptchaUtil();
StringBuffer code = new StringBuffer();
BufferedImage image = tool.genRandomCodeImage(code);
session.removeAttribute(Constant.CAPTCHA_SESSION_KEY);
session.setAttribute(Constant.CAPTCHA_SESSION_KEY, code.toString());
// 将内存中的图片通过流动形式输出到客户端
ImageIO.write(image, "JPEG", response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.10.修改登录页面
<div class="form-group">
<label for="captcha">验证码:</label>
<input class="required" id="captcha" name="captcha" size="10"
tabindex="2" th:field="*{captcha}" autocomplete="off"/>
<img th:src="@{/captcha.jpg}" id="captcha_img" onclick="javascript:refreshCaptcha()"/>
</div>
<script type="text/javascript">
function refreshCaptcha(){
$("#captcha_img").attr("src","/cas/captcha.jpg?id=" + new Date() + Math.floor(Math.random()*24));
}
</script>
2.11.注释默认登录逻辑
#cas.authn.accept.users=admin::123456
参考:https://blog.csdn.net/qq_34021712/article/details/82259101


浙公网安备 33010602011771号