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

jira配错ldap后无法登录_spring

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;
}