Jira接入SSO单点登录
- 1、导入对应的jar包
- 2、创建对应的目录和文件
- 3、重写方法和工具类CuSSOUtil
- 4、接入sso与不接入sso之前的区别
- 4.1、重写getUser()方法(一种是不接入sso,一种接入sso)
- 4.1.1、不接入sso:
- 4.1.2、重写不接入sso的方法后,访问jira
- 4.1.3、 接入sso(未解决问题前的代码)
- 4.1.4、 解决样式缺失,令牌失效,会话过期的问题
- 4.1.5、 解决登出,统一登出,外部系统调用jira增删查改接口的问题
- 4.1.6、 接入sso(该版本是已解决问题后的版本)
- 4.1.7、重写接入sso的方法后,访问jira
- 4.1.8、重写登出方法logout()
1、导入对应的jar包
atlassian-seraph-4.0.4.jar
embedded-crowd-api-4.1.6.jar
jira-api-8.13.3.jar
2、创建对应的目录和文件
com.atlassian.jira.security.cuslogin
public class CusJiraSeraphAuthenticator extends DefaultAuthenticator
3、重写方法和工具类CuSSOUtil
// jira登录方法
getUser(HttpServletRequest request, HttpServletResponse response)
// jira登出方法
logout(HttpServletRequest request, HttpServletResponse response)
// 工具类
package com.atlassian.jira.security.cuslogin;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import sun.misc.BASE64Encoder;
import java.io.*;
import java.util.Map;
/**
* 〈Function overview〉<br>
* @description: 获取 token,用户信息的方式
* @className: CusSSOUtil
*/
public class CusSSOUtil {
//应用id
public static final String clientId = "";
//应用密钥
public static final String clientSecret = "";
//回调地址
public static final String redirectUri = "http://ip:port";
//授权中心地址
public static final String uaaUri = "http://ip:port/api-uaa/oauth/authorize";
//令牌获取地址
public static final String tokenUri = "http://ip:port/api-uaa/oauth/token";
//用户信息获取地址
public static final String currentUri = "http://ip:port/api-user/users/current";
//用户登出地址
public static final String logoutUri = "http://ip:port/api-uaa/oauth/remove/token";
/**
* 获取token
*/
public static Map getAccessToken(String code) throws UnsupportedEncodingException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
byte[] authorization = (clientId + ":" + clientSecret).getBytes("UTF-8");
BASE64Encoder encoder = new BASE64Encoder();
String base64Auth = encoder.encode(authorization);
headers.add("Authorization", "Basic " + base64Auth);
MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
param.add("code", code);
param.add("grant_type", "authorization_code");
param.add("redirect_uri", redirectUri);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(param, headers);
ResponseEntity<Map> response = restTemplate.postForEntity(tokenUri, request , Map.class);
Map result = response.getBody();
return result;
}
/**
* 获取用户信息
*/
public static Map getUserInfo(String accessToken) {
RestTemplate restTemplate = new RestTemplate();
Map result = restTemplate.getForObject(currentUri+"?access_token="+accessToken, Map.class);
return (Map)result.get("data");
}
}
4、接入sso与不接入sso之前的区别
写不接入sso的重写方法,主要是为了和接入sso进行对比,看看接入sso后会有什么问题
以下是可能出现的问题:
1、接入sso后界面出现样式缺失
2、无法登出与无法统一登出
3、令牌缺失,会话过期
4、外部系统调用jira接口需要在请求头中配置(headers.add("X-Atlassian-Token", "no-check");)
4.1、重写getUser()方法(一种是不接入sso,一种接入sso)
4.1.1、不接入sso:
public Principal getUser(HttpServletRequest request, HttpServletResponse response) {
String username = null;
if (null == request.getParameter("username")) {
username = (String) request.getSession().getAttribute("username");
} else {
username = request.getParameter("username");
}
log.info("请求url地址:" + request.getRequestURL().toString());
Principal user = null;
try {
log.info("登录用户名:" + username);
//根据用户名生成Principal对象
user = getUser(username);
if (null != user) {
//单点登录成功,设置jira单点的信息
log.info("Logged in via SSO, with User " + user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
request.getSession().setAttribute("username", username);
} else {
//匹配jira用户失败
log.info("get not user");
}
} catch (Exception e) // catch class cast exceptions
{
log.warn("Exception: " + e, e);
}
return user;
}
4.1.2、重写不接入sso的方法后,访问jira
url: http://ip:port/?username=登录用户名
4.1.3、 接入sso(未解决问题前的代码)
public Principal getUser(HttpServletRequest request, HttpServletResponse response) {
log.info("请求的url地址:" + request.getRequestURL().toString());
Principal user = null;
try {
//生成封装request后的cusSSOObj对象
Object usernameObj = request.getSession().getAttribute("username");
Object token = request.getSession().getAttribute("access_token");
// 从请求中获取参数code
String code = request.getParameter("code");
if (code != null) {
// 用code交换token
Map accessTokenObj = CusSSOUtil.getAccessToken(code);
if (accessTokenObj != null) {
String accessToken = (String) accessTokenObj.get("access_token");
// 用token交换用户信息
Map userInfo = CusSSOUtil.getUserInfo(accessToken);
String username = userInfo.get("username").toString();
//判断是否取到用户名
if (username != null) {
//根据用户名生成Principal对象
user = getUser(username);
//判断是否映射到jira用户
if (null != user) {
//单点登录成功,设置jira单点的信息
log.info("Logged in via SSO, with User " + user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
request.getSession().setAttribute("username", username);
} else {
//匹配jira用户失败
log.info("get not user");
}
}
} else {
log.info("获取token失败,重定向至sso登录页");
response.sendRedirect(CusSSOUtil.uaaUri + "?client_id=" + CusSSOUtil.clientId + "&redirect_uri=" + CusSSOUtil.redirectUri + "&response_type=code");
}
} else {
if (token != null) {
Map userInfo = null;
log.info("token不为空");
Boolean syncLogoutOrCancel = null;
try {
userInfo = CusSSOUtil.getUserInfo((String) token);
} catch (Exception e) {
e.printStackTrace();
} finally {
syncLogoutOrCancel = null == userInfo;
}
if (syncLogoutOrCancel) {
log.info("第三模块,其它系统已登出,jira也进行重定向登出");
logout(request, response);
} else {
if (("/logout").equals(request.getRequestURI())) {
log.info("进行注销操作");
} else {
log.info("已进行过登录,允许请求资源");
user = getUser(usernameObj.toString());
}
}
} else {
if (("/").equals(request.getRequestURI())) {
log.info("定时三秒刷新浏览器");
response.setHeader("refresh", "0;URL=" + CusSSOUtil.uaaUri + "?client_id=" + CusSSOUtil.clientId + "&redirect_uri=" + CusSSOUtil.redirectUri + "&response_type=code");
} else {
log.info("无登录状态,重定向至登录页");
response.sendRedirect(CusSSOUtil.uaaUri + "?client_id=" + CusSSOUtil.clientId + "&redirect_uri=" + CusSSOUtil.redirectUri + "&response_type=code");
}
return null;
}
}
} catch (Exception e) // catch class cast exceptions
{
log.warn("Exception: " + e, e);
}
return user;
}
}
4.1.4、 解决样式缺失,令牌失效,会话过期的问题
1、对请求中的不需要经过sso的流程的url进行过滤,让它们仍走不接入sso的流程
if (request.getRequestURL().toString().contains("/rest/") || request.getRequestURL().toString().contains("/plugins/")
|| request.getRequestURL().toString().contains("/images/") || request.getRequestURL().toString().contains(".css")
|| request.getRequestURL().toString().contains(".woff") || request.getRequestURL().toString().contains(".gif")
|| request.getRequestURL().toString().contains("/useravatar")) {
Object usernameObj = request.getSession().getAttribute("username");
Principal user = null;
//根据用户名生成Principal对象
user = getUser((String) usernameObj);
try {
if (null != user) {
//单点登录成功,设置jira单点的信息
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
request.getSession().setAttribute("username", usernameObj);
} else {
//匹配jira用户失败
log.info("get not user");
}
} catch (Exception e) {
e.printStackTrace();
}
return user;
}
4.1.5、 解决登出,统一登出,外部系统调用jira增删查改接口的问题
1、 请求中的js需要放过,让它走不接入sso的流程
2、 请求中的alreadyloggedout.jsp需要拦截,并让他执行登出操作,解决登出的问题
3、 请求中的jspa文件,在其它系统进行登出操作后,会将redis中的token清除掉(包括jira的),这时刷新jira页面,会重写加载jspa文件,我们需要拦截请求,并使用已失效的token去交换用户信息,导致报错,我们拦截报错,让它执行登出操作,重定向sso登录页
4、 请求中的AddUser.jspa等jspa文件,对应jira的用户新增,修改,查询,删除接口,需要使用管理员用户进行登录操作,
else if (request.getRequestURL().toString().contains(".js")) {
Object usernameObj = request.getSession().getAttribute("username");
Principal user = null;
//根据用户名生成Principal对象
user = getUser((String) usernameObj);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
if (request.getRequestURL().toString().contains(".jsp")) {
if (request.getReqequestURL().toString().contains(".jsp")) {
if (request.getRequestURL().toString().contains("/alreadyloggedout.jsp")) {
try {
logout(request, response);
} catch (AuthenticatorException e) {
e.printStackTrace();
}
}
}
if (request.getRequestURL().toString().contains(".jspa")) {
if (null!=request.getSession().getAttribute("access_token")){
Map userInfo = null;
Boolean syncLogoutOrCancel = null;
try {
userInfo = CusSSOUtil.getUserInfo((String) request.getSession().getAttribute("access_token"));
} catch (Exception e) {
e.printStackTrace();
} finally {
syncLogoutOrCancel = null == userInfo;
}
if (syncLogoutOrCancel){
try {
log.info("其它系统已登出,jira也进行重定向登出");
logout(request, response);
} catch (AuthenticatorException e) {
e.printStackTrace();
}
try {
if (request.getRequestURL().toString().contains("AddUser.jspa") || request.getRequestURL().toString().contains("UserBrowser.jspa")
|| request.getRequestURL().toString().contains("EditUser.jspa") || request.getRequestURL().toString().contains("DeleteUser.jspa")) {
Principal changeUser = null;
changeUser = getUser(jira管理员用户名);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
request.getSession().setAttribute("username", usernameObj);
return changeUser;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return user;
}
4.1.6、 接入sso(该版本是已解决问题后的版本)
if (request.getRequestURL().toString().contains("/rest/") || request.getRequestURL().toString().contains("/plugins/")
|| request.getRequestURL().toString().contains("/images/") || request.getRequestURL().toString().contains(".css")
|| request.getRequestURL().toString().contains(".woff") || request.getRequestURL().toString().contains(".gif")
|| request.getRequestURL().toString().contains("/useravatar")) {
log.info("页面:" + request.getRequestURL().toString());
Object usernameObj = request.getSession().getAttribute("username");
Principal user = null;
//根据用户名生成Principal对象
user = getUser((String) usernameObj);
try {
if (null != user) {
//单点登录成功,设置jira单点的信息
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
request.getSession().setAttribute("username", usernameObj);
} else {
//匹配jira用户失败
log.info("get not user");
}
} catch (Exception e) {
e.printStackTrace();
}
return user;
} else if (request.getRequestURL().toString().contains(".js")) {
Object usernameObj = request.getSession().getAttribute("username");
Principal user = null;
//根据用户名生成Principal对象
user = getUser((String) usernameObj);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
if (request.getRequestURL().toString().contains(".jsp")) {
if (request.getReqequestURL().toString().contains(".jsp")) {
if (request.getRequestURL().toString().contains("/alreadyloggedout.jsp")) {
try {
logout(request, response);
} catch (AuthenticatorException e) {
e.printStackTrace();
}
}
}
if (request.getRequestURL().toString().contains(".jspa")) {
log.info("jspa资源页面:" + request.getRequestURL().toString());
if (null!=request.getSession().getAttribute("access_token")){
Map userInfo = null;
Boolean syncLogoutOrCancel = null;
try {
userInfo = CusSSOUtil.getUserInfo((String) request.getSession().getAttribute("access_token"));
} catch (Exception e) {
e.printStackTrace();
} finally {
syncLogoutOrCancel = null == userInfo;
}
if (syncLogoutOrCancel){
try {
log.info("其它系统已登出,jira也进行重定向登出");
logout(request, response);
} catch (AuthenticatorException e) {
e.printStackTrace();
}
try {
if (request.getRequestURL().toString().contains("AddUser.jspa") || request.getRequestURL().toString().contains("UserBrowser.jspa")
|| request.getRequestURL().toString().contains("EditUser.jspa") || request.getRequestURL().toString().contains("DeleteUser.jspa")) {
Principal changeUser = null;
changeUser = getUser(jira管理员用户名);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
request.getSession().setAttribute("username", usernameObj);
return changeUser;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return user;
}else {
log.info("请求的url地址:" + request.getRequestURL().toString());
Principal user = null;
try {
//生成封装request后的cusSSOObj对象
Object usernameObj = request.getSession().getAttribute("username");
Object token = request.getSession().getAttribute("access_token");
String code = request.getParameter("code");
if (code != null) {
log.info("code信息:" + code);
Map accessTokenObj = CusSSOUtil.getAccessToken(code);
if (accessTokenObj != null) {
String accessToken = (String) accessTokenObj.get("access_token");
log.info("accessToken信息:" + accessToken);
Map userInfo = CusSSOUtil.getUserInfo(accessToken);
String username = userInfo.get("username").toString();
log.info("用户信息:" + username);
//判断是否取到用户名
if (username != null) {
//根据用户名生成Principal对象
user = getUser(username);
//判断是否映射到jira用户
if (null != user) {
//单点登录成功,设置jira单点的信息
log.info("Logged in via SSO, with User " + user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
request.getSession().setAttribute("username", username);
request.getSession().setAttribute("access_token", accessToken);
} else {
//匹配jira用户失败
log.info("get not user");
}
}
} else {
log.info("获取token失败,重定向至sso登录页");
response.sendRedirect(CusSSOUtil.uaaUri + "?client_id=" + CusSSOUtil.clientId + "&redirect_uri=" + CusSSOUtil.redirectUri + "&response_type=code");
}
} else {
if (token != null) {
Map userInfo = null;
log.info("token不为空");
Boolean syncLogoutOrCancel = null;
try {
userInfo = CusSSOUtil.getUserInfo((String) token);
} catch (Exception e) {
e.printStackTrace();
} finally {
syncLogoutOrCancel = null == userInfo;
}
if (syncLogoutOrCancel) {
log.info("其它系统已登出,jira也进行重定向登出");
logout(request, response);
} else {
if (("/logout").equals(request.getRequestURI())) {
log.info("进行注销操作");
} else {
log.info("已进行过登录,允许请求资源");
user =getUser(usernameObj.toString());
}
}
} else {
if (("/").equals(request.getRequestURI())) {
log.info("定时0秒刷新浏览器");
response.setHeader("refresh", "0;URL=" + CusSSOUtil.uaaUri + "?client_id=" + CusSSOUtil.clientId + "&redirect_uri=" + CusSSOUtil.redirectUri + "&response_type=code");
} else {
log.info("无登录状态,重定向至登录页");
response.sendRedirect(CusSSOUtil.uaaUri + "?client_id=" + CusSSOUtil.clientId + "&redirect_uri=" + CusSSOUtil.redirectUri + "&response_type=code");
}
return null;
}
}
} catch (Exception e) // catch class cast exceptions
{
log.warn("Exception: " + e, e);
}
return user;
}
4.1.7、重写接入sso的方法后,访问jira
url: http://ip:port/
4.1.8、重写登出方法logout()
public boolean logout(HttpServletRequest request, HttpServletResponse response) throws AuthenticatorException {
String accessToken = (String) request.getSession().getAttribute("access_token");
String username = (String) request.getSession().getAttribute("username");
log.info("进入退出接口");
Map<String, String> map = new HashMap<>();
map.put("access_token", accessToken);
try {
log.info("进行登录重定向");
request.getSession().removeAttribute("access_token");
request.getSession().removeAttribute("username");
response.sendRedirect(CusSSOUtil.logoutUri + "?redirect_uri="+CusSSOUtil.redirectUri);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}