本文用最简单的方法来集成,不引入第三方权限框架,用最小改动,换取以后升级的便利。
需要集成的是xxl-job-admin项目,如果有小伙伴还没有部署XXL-JOB,
可以看我的一个简版部署教程:
XXL-JOB的权限设置比较简单,用的是拦截器,所以我们就在这个基础上来集成CAS:
1.pom.xml引用cas快速配置包:
<!-- CAS依赖包 -->
<dependency>
<groupId>net.unicon.cas</groupId>
<artifactId>cas-client-autoconfig-support</artifactId>
<version>1.6.0-GA</version>
</dependency>
2.application.properties配置:
# cas.这些参数是 cas-client-autoconfig-support 包里用的。
cas.server-url-prefix=https://casserver.com/casserver/
cas.server-login-url=https://casserver.com/casserver/login
cas.client-host-url=http://localhost:8080/xxl-job-admin
cas.validation-type=CAS
cas.use-session=true
cas.redirect-after-validation=true
# cas-这些参数是 做cas集成时 自定义的,目的是代码里不写死,方便统一配置。
cas-ignore-pattern=(/api/*)|(/file/*)|(/js/*)|(/img/*)|(/view/*)|(/css/*)|(/static/*)
cas-login-url=https://casserver.com/casserver/login?service=http://localhost:8080/xxl-job-admin/toLoginCas
cas-logout-url=https://casserver.com/casserver/logout?service=http://localhost:8080/xxl-job-admin
3.启动类XxlJobAdminApplication:
增加注解 @EnableCasClient
@SpringBootApplication
@EnableCasClient // 开启CAS支持
public class XxlJobAdminApplication {
public static void main(String[] args) {
SpringApplication.run(XxlJobAdminApplication.class, args);
}
}
4.LoginService增加一个方法loginCas:
/**
* cas登陆方法
*
* @author hubg 2021-10-19 14:39:12
*/
public ReturnT<String> loginCas(HttpServletRequest request, HttpServletResponse response, String username) {
// param
if (username == null || username.trim().length() == 0) {
return new ReturnT<String>(500, I18nUtil.getString("login_param_empty"));
}
XxlJobUser xxlJobUser = xxlJobUserDao.loadByUserName(username);
if (xxlJobUser == null) {
return new ReturnT<String>(500, I18nUtil.getString("login_param_unvalid"));
}
String loginToken = makeToken(xxlJobUser);
// do login
CookieUtil.set(response, LOGIN_IDENTITY_KEY, loginToken, false);
return ReturnT.SUCCESS;
}
5.LoginService修改ifLogin方法,注掉密码验证;修改logout方法,回传cas登出页连接:
public XxlJobUser ifLogin(HttpServletRequest request, HttpServletResponse response) {
String cookieToken = CookieUtil.getValue(request, LOGIN_IDENTITY_KEY);
if (cookieToken != null) {
XxlJobUser cookieUser = null;
try {
cookieUser = parseToken(cookieToken);
} catch (Exception e) {
logout(request, response);
}
if (cookieUser != null) {
XxlJobUser dbUser = xxlJobUserDao.loadByUserName(cookieUser.getUsername());
if (dbUser != null) {
//if (cookieUser.getPassword().equals(dbUser.getPassword())) {
return dbUser;
//}
}
}
}
return null;
}
@Value("${cas-logout-url}")
private String casLogoutUrl;
/**
* logout
*
* @param request
* @param response
*/
public ReturnT<String> logout(HttpServletRequest request, HttpServletResponse response) {
CookieUtil.remove(request, response, LOGIN_IDENTITY_KEY);
ReturnT<String> rt = new ReturnT<String>(null);
// 登出之后,将cas登出url传给的登出页。
// 这么做主要目的是为了不写死url,可以在配置文件里统一配置。
rt.setMsg(casLogoutUrl);
return rt;
}
6.IndexController增加2个方法 toLoginCas,getCasUser:
用于获取cas相关信息及处理登陆事务,注意为了处理多公司的情况,这里的用户名是
“姓名.公司域”。如"zhangsan.yx","lisi.tc"。
/**
* 拼接用户和公司域
*/
public String getCasUser(HttpServletRequest request) {
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
if (principal == null) {
return null;
}
String username = (String) principal.getAttributes().get("username");
String company = (String) principal.getAttributes().get("company");
return username + "." + company;
}
@Value("${cas-logout-url}")
private String casLogoutUrl;
@RequestMapping("/toLoginCas")
@PermissionLimit(limit = false)
public ModelAndView toLoginCas(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) {
String userName = getCasUser(request);
if (userName == null) {
//cas用户名为空,要跳转到cas登出页,让他退出重新登陆。
return new ModelAndView("redirect:"+casLogoutUrl);
}
loginService.loginCas(request, response, userName);
if (loginService.ifLogin(request, response) != null) {
modelAndView.setView(new RedirectView("/", true, false));
return modelAndView;
}
return new ModelAndView("/");
}
7.权限拦截器PermissionInterceptor改动preHandle:
修改 用户验证为空时 的处理逻辑,跳转到cas登陆页
@Value("${cas-login-url}")
private String casLoginUrl;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return super.preHandle(request, response, handler);
}
// if need login
boolean needLogin = true;
boolean needAdminuser = false;
HandlerMethod method = (HandlerMethod) handler;
PermissionLimit permission = method.getMethodAnnotation(PermissionLimit.class);
if (permission != null) {
needLogin = permission.limit();
needAdminuser = permission.adminuser();
}
if (needLogin) {
XxlJobUser loginUser = loginService.ifLogin(request, response);
if (loginUser == null) {
response.setStatus(302);
// 跳转到cas登陆页
response.setHeader("location", casLoginUrl);
return false;
}
if (needAdminuser && loginUser.getRole() != 1) {
throw new RuntimeException(I18nUtil.getString("system_permission_limit"));
}
request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);
}
return super.preHandle(request, response, handler);
}
8.修改js代码static/js/common.1.js和user.index.1.js
common.1.js,用户退出时指向cas logout页,退出成功跳转到主页,可以重新登陆。
只修改window.location.href值
$.post(base_url + "/logout", function(data, status) {
if (data.code == "200") {
layer.msg( I18n.logout_success );
setTimeout(function(){
// 这个是把配置文件中的cas登出地址传过来了。
window.location.href = data.msg;
}, 500);
} else {
layer.open({
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.logout_fail),
icon: '2'
});
}
}
user.index.1.js修改一下校验规则,正则增加 \\. ,允许新建用户名时,输入"."
jQuery.validator.addMethod("myValid01", function(value, element) {
var length = value.length;
// var valid = /^[a-z][a-z0-9]*$/;
var valid = /^[a-z][a-z0-9\\.]*$/;
return this.optional(element) || valid.test(value);
}, I18n.user_username_valid );
9.增加一个配置类,忽略掉不需要CAS验证的连接(如执行机的访问API、js、图片等)
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class CasUrlPatternConfig {
@Value("${cas.server-login-url}")
private String casServerLoginUrl;
@Value("${cas.client-host-url}")
private String casClientHostUrl;
@Value("${cas-ignore-pattern}")
private String casIgnorePattern;
/**
* description:授权过滤器
* ignoreUrlPatternType 使用CAS现成的正则表达式过滤策略
*/
@Bean
public FilterRegistrationBean filterAuthenticationRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new AuthenticationFilter());
registration.addUrlPatterns("/*");
Map<String,String> initParameters = new HashMap<String, String>();
initParameters.put("casServerLoginUrl", casServerLoginUrl);
initParameters.put("serverName", casClientHostUrl);
//配置文件中设置要忽略拦截的路径
initParameters.put("ignorePattern", casIgnorePattern);
initParameters.put("ignoreUrlPatternType", "org.jasig.cas.client.authentication.RegexUrlPatternMatcherStrategy");
registration.setInitParameters(initParameters);
registration.setOrder(1);
return registration;
}
}
10.导入用户信息,xxl_job_user用户表结构不动,只是把用户和公司信息拼到username字段里就行了:
hubg.yx
hubaoguo123.tc
注:如果只有一个公司,不会出现用户重复情况,可以不拼公司名,但注意toLoginCas里的用户名获取方法要改一下。
11.到这里XXL-JOB集成CAS单点登录完成了,上两个图:
总结:
这样集成的好处就是,代码改动最小化,不用集成三方的权限框架,对原代码侵入小,如果xxl_job官方更新版本,几乎是可以无缝升级的。
如大家集成时遇到什么问题,欢迎留言沟通!!