SpringBoot前后端分离整合cas
项目前端使用nginx启动,后端是多个springBoot应用;
nginx可以统一管理Cookie,避免出现跨域问题。
1、添加依赖
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-support-springboot</artifactId>
<version>3.6.2</version>
</dependency>
2、配置文件添加信息
cas:
# cas服务
server-url-prefix: http://cas.com/cas
# cas登录
server-login-url: http://cas.com/cas/login
validation-type: cas
# 自己系统登录地址
client-host-url: http://localhost:9080
# 不校验权限路径
ignorePattern: /admin/api/pvs
# cas回调地址
casLogin-url: http://cas.com/cas/login?service=http://localhost:9080/admin/loginApi/casLogin
# cas登出地址,需要专门登出时使用,系统登出时跳转到这个地址
casLogout-url: http://cas.com/cas/logout?service=http://localhost:9080
启动类添加@EnableCasClient
@EnableCasClient
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args);
}
}
3、重写cas的重定向和授权
package com.hand.aml.config;
// 保存cas回调地址
public class CasParam {
public static String casLoginUrl;
}
使用ignorePattern添加不需要授权的url,如登出方法的url
@Configuration
public class SmsCasClientConfigurer implements CasClientConfigurer {
@Value("${host-url}")
private String serverName;
@Value("${ignorePattern}")
private String ignorePattern;
@Value("${cas.server-login-url}")
private String serverLoginUrl;
@Value("${casLogin-url}")
private String casLoginUrl;
@Override
public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) {
Map initParameters = authenticationFilter.getInitParameters();
initParameters.put("authenticationRedirectStrategyClass", "com.hand.aml.config.SmsAuthenticationRedirectStrategy");
// 设置不校验权限路径
initParameters.put("ignorePattern", ignorePattern);
CasParam.casLoginUrl= casLoginUrl;
}
}
重写AuthenticationRedirectStrategy,直接给前端返回401错误码:
前后端分离项目,cas的重定向无法跳转到cas登录网址去,需要前端进行跳转,所以要重写AuthenticationRedirectStrategy。
package com.hand.aml.config;
import com.alibaba.fastjson2.JSONObject;
import org.jasig.cas.client.authentication.AuthenticationRedirectStrategy;
import org.springframework.http.HttpStatus;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class SmsAuthenticationRedirectStrategy implements AuthenticationRedirectStrategy {
` @Override
public void redirect(HttpServletRequest request, HttpServletResponse response, String potentialRedirectUrl) throws IOException {
System.out.println("SmsAuthenticationRedirectStrategy");
response.setCharacterEncoding("UTF-8");
//response.setContentType("application/json;charset = UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
//响应数据 JSONObject jsonObject = new JSONObject();
String s = CasParam.casLoginUrl;
jsonObject.put("casLoginUrl", s);
// 输出
PrintWriter out = response.getWriter();
out.write(jsonObject.toString());
}``
}
前端添加代码,处理返回的401错误码:
if (response.status == 401) {
var responseText = jQuery.parseJSON(response.responseText);
if (responseText !=null && responseText.casLoginUrl !=null) {
// 跳转到cas登录地址
location.href = responseText.casLoginUrl;
}
}
四、Nginx配置
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user - forward [$time_local] "$request" $upstream_addr '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
server_tokens off;
keepalive_timeout 65;
#gzip on;
server {
listen 9080;
server_name localhost;
proxy_read_timeout 600s;
charset utf-8;
client_header_buffer_size 2M;
large_client_header_buffers 4 2M;
location / {
root html;
index index.html index.htm;
}
location = /vpf/aml/admin/ {
proxy_pass http://localhost:9300;
client_max_body_size 1024M;
client_body_buffer_size 1024M;
}
error_page 404 /404.html;
error_page 413 /413.json;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
client_max_body_size 1024M;
root html;
}
}
}
五、大致流程
我们先访问自己系统的地址,首先会调用isLoginedOn方法判断是否登录,如果未登录,会被cas拦截,返回401,前端跳转到cas登录页面,cas登录成功后,会调用回调方法,即casLogin方法,在这个方法里再进行自己系统的登录。
/**
* 判断当前是否有登录信息.
* @param req http请求
* @param rep http应答
* @return Y-有登录信息 N+token-无登陆信息返回用于加密的token
*/
@RequestMapping("/isLoginedOn")
public String isLoginedOn(HttpServletRequest req, HttpServletResponse rep) {
Principal principal = req.getUserPrincipal();
String key = CookieUtil.getCookieKey(req);
if (principal == null) {
return casLoginUrl;
}
if (key == null || redisService.get(key, TokenJson.class) == null) {
key = CookieUtil.createAmlCookie(req, rep);
return casLoginUrl;
}
return "Y";
}
@RequestMapping("/casLogin")
public String login(HttpServletRequest req, HttpServletResponse response) {
// cas登陆成功后,principal中会有对于用户信息
Principal principal = req.getUserPrincipal();
if (principal != null) {
String name = principal.getName();
User user = userService.getUserByUserName(name);
if (user == null) {
return failure("NOT_EXIST");
}
TokenJson json = new TokenJson();
json.setBranchCode(user.getBranchCode());
json.setId(user.getId());
json.setName(user.getUserName());
json.setFullName(user.getFullName());
json.setRoleCode(user.getRole().getRoleCode());
json.setRoleName(user.getRole().getRoleName());
if (user.getRole().getPermissions() != null) {
user.getRole().getPermissions().forEach(e -> json.getCodes().add(e.getPermissionCode()));
}
if (user.getLocale() == null) {
user.setLocale("zh_CN");
}
if (user.getTheme() == null) {
user.setTheme("ext-all-neptune.css");
}
String key = CookieUtil.createAmlCookie(req, response);
CookieUtil.createAmlDefaultCookie(response, CookieUtil.LOCALE_COOKIE_KEY, user.getLocale(), CookieUtil.LOCALE_COOKIE_VALUE_ZH_CN);
CookieUtil.createAmlDefaultCookie(response, CookieUtil.STYLE_COOKIE_KEY, user.getTheme(), CookieUtil.STYLE_COOKIE_VALUE_NEPTUNE);
redisService.set(key, json, sessionTimeout);
redisService.set(RedisKey.SESSION_KEY_TOKEN + user.getUserName(), key, sessionTimeout);
redisService.set(RedisKey.SESSION_KEY_LOCALE + user.getUserName(), user.getLocale(), sessionTimeout);
user.setAutoFill(false);
userService.saveOrUpdate(user);
traceService.log(user.getContactName(), "LOGIN_LOGOUT.LOGIN", user.getContactName());
loginSuccess(user.getTheme(), user.getLocale(), "loginSuccess");
return "<script>location.href='/prod/htm/app/main.html?single=true';</script>";
}
return failure();
}