idea基于springboot+redis+springSession+jsp实现简易SSO单点登录

现象:

web系统早已从久远单系统过度到现在的多系统,面对多系统时难道要用户一个一个的登录,如下图:

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第1张图片

系统的负责性由系统自己负责,而不是需要用户负责,无论多复杂的系统的对用户而言就是一个整体,也就是说用户访问web应用群要像单系统一样一次登录,一次注销就ok,如图:

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第2张图片

单系统登录的解决方案虽然完美,单系统登录的核心是cookie,cookie会携带会话id在浏览器与web服务器保持会话,但是有一定限制就是cookie域(一般与网站的域名所对应),http访问网站是会携带与这个网站域说匹配的cookie,而不是所有cookie,早期的单点登录就是基于cookie共享,但是可行性不好,首先这种方式要保证群应用的顶级域名一致,其次要保证服务器开发技术要一致不然cookie的key值(tomcat为sessionid)不一致,无法保证会话,共享cookie不支持跨语言的技术平台登录,如java、php、.net,还有cookie本身就是不安全的 

所以需要一种更好的解决方案也就是单点登录

名词解释:

单点登录:单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录

相对于单系统登录,sso提供一个认证中心,只有认证中心接受用户名/或密码等安全信息,子系统不提供登录入口,只接受认证中心的间接授权,间接授权使用令牌实现(token),当用户登录某一个子系统,会转到认证中心,认证中心会验证用户名/密码等安全信息,验证通过生成令牌(token),生成的令牌会作为参数发送给各个子系统,子系统根据拿到的令牌(token)访问受保护的资源,当操作子系统其它资源时都会拿这个令牌(token)到认证中心验证,这个过程就是单点登录,如下图:

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第3张图片

上图描述:

1、当用户通过浏览器访问系统1,拦截器发现用户并没有登录,跳转到认证中心,并将请求地址作为参数

2、sso认证中心首先会检查是否携带token,携带token跳转到token验证,没有就跳转到认证中心登陆界面

3、认证通过后生产t oken,产生全局会话,并拼接url(加上token)重定向到系统1地址

4、系统1地址会携带token,之后再重新执行token验证方法,验证通过直接跳转到系统1首页

5、访问系统2时,将携带token,此时执行token验证方法,验证通过后放行

以下就一步一步实现单点登录,使用开发工具idea、使用技术springBoot+redis+springSession+jsp实现

1、新建项目使用spring.io提供的模板

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第4张图片

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第5张图片

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第6张图片

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第7张图片

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第8张图片

2、首先要解决springBoot访问jsp问题,springBoot官方建议使用 theamleaf,如果使用想要使用jsp就要添加如下几个依赖

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第9张图片

3、加入spring-redis主键依赖

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第10张图片

工程搭建完毕,编码实现,首先是ssoserver(因为是简易版的所以sso-client与sso-server组合在一起)

sso-server:

    1、拦截子系统的未等录的请求,跳转至认证中心

    2、校验携带的token是否有效

    3、拦截子系统注销请求,销毁会话

    4、验证用户登陆信息

    5、创建令牌

    6、创建与子系统间的会话

下面来实现ssoserver吧

ssoserver的工程构成:

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第11张图片

1、springSession配置

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第12张图片

2、ssoserver作用就是拦截子系统的请求所以使用filter实现:

public class SsoFilter implements Filter {

    //sso认证
    private final String SSO_SERVER_URL = "http://localhost:8080/sso/auth";
    //sso token 验证
    private final String SSO_VERIFI_URL="http://localhost:8080/sso/verifi";
    //sso注销
    private final String SSO_LOGINOUT_URL="http://localhost:8080/sso/logout";
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //检查是否携带token.
        HttpServletRequest httpServletRequest =(HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        String token = httpServletRequest.getParameter("token");
        Map info = new HashMap<>();
        //token不为空就验证
        if(token != null){
            boolean flag = verifi(servletRequest,SSO_VERIFI_URL,token);
            if (flag){
                filterChain.doFilter(servletRequest,servletResponse);
                return;
            }else{
                info.put("info",token+"无效");
                info.put("code","10010");
                httpServletResponse.getWriter().write((Json2String(info)));
                return;
            }
        }
        //如果有效并且已登录就放行到下个过滤器
        HttpSession session=httpServletRequest.getSession();
        if (session.getAttribute("flag")!=null && (boolean)session.getAttribute("flag") == true){
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }
        //获取注销的标记销毁全局会话
        String loginOut = httpServletRequest.getParameter("logout");
        if (loginOut == "true"){
            httpServletResponse.sendRedirect(SSO_LOGINOUT_URL);
        }
        //没有token就跳转到认证中心
        //当前请求地址
        String callBackUrl = httpServletRequest.getRequestURL().toString();
        StringBuilder authUrl = new StringBuilder();
        authUrl.append(SSO_SERVER_URL).append("?callBackUrl=").append(callBackUrl);
        httpServletResponse.sendRedirect(authUrl.toString());

    }




    @Override
    public void destroy() {

    }
    //验证token方法
    private boolean verifi(ServletRequest servletRequest,String url,String token){
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(url).append("?token=").append(token);
        //封装的ResTemplateUtil工具类发送get请求
        String result = RestTemplateUtil.get(servletRequest,stringBuilder.toString(),null);
        JSONObject jsonObject=JSONObject.parseObject(result);
        if ( jsonObject.getString("code").equals("200")){
            return  true;
        }else{
            return false;
        }
    }

    private String Json2String(Object obj){
        if (obj == null){
            return null;
        }
       String jsonString= JSONObject.toJSONString(obj);
        return jsonString;
    }

3、restTemplateutil封装,RestTemplate、 ResponseEntity、HttpEntity等用法自行百度:

public class RestTemplateUtil {

    private static RestTemplate restTemplate = new RestTemplate();

    public static String get(ServletRequest request,String url,Map params){
        ResponseEntity responseEntity = request(request,url,HttpMethod.GET,params);
        return responseEntity.getBody();
    }

    public static String post(ServletRequest request,String url,Map params){
        ResponseEntity responseEntity = request(request,url,HttpMethod.POST,params);
        return responseEntity.getBody();
    }


    private static ResponseEntity request(ServletRequest request, String url, HttpMethod method,Map parmas){
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpHeaders httpHeaders = new HttpHeaders();
        Enumeration enumerations = httpServletRequest.getHeaderNames();
        while (enumerations.hasMoreElements()){
            String key =(String)enumerations.nextElement();
            String value = httpServletRequest.getHeader(key);
            httpHeaders.add(key,value);
        }
        HttpEntity httpEntity = new HttpEntity(parmas == null?null: JSONObject.toJSONString(parmas),httpHeaders);
        ResponseEntity res = restTemplate.exchange(url,method,httpEntity,String.class);
        return res;
    }
}

4、认证与验证中心:

public class AuthController {
   //认证并生成令牌(token)
    @RequestMapping("/auth")
    public String auth(String userName, String password,String callBackUrl, Model model, HttpSession session,HttpServletRequest request){
        //生成token
        String token = UUID.randomUUID().toString().substring(0,16);
        if (userName == null&&password == null){
            return "login";
        }
        if ("admin".equals(userName) && "admin".equals(password)){
            //记录登录状态
            session.setAttribute("flag",true);
            session.setAttribute("token",token);
            return "index";
        }else{
            model.addAttribute("error","用户名或密码错误");
            return "login";
        }

    }
   //验证令牌(token)
    @RequestMapping("/verifi")
    @ResponseBody
    public JSONObject vifer(HttpServletRequest request,HttpSession session){
        //获取token
        String authToken = request.getParameter("token");
        String token = (String) session.getAttribute("token");
        JSONObject jsonObject = new JSONObject();
        if (token.equals(authToken) && token !=null){
            jsonObject.put("code","200");
            jsonObject.put("info","token认证成功");
        }else{
            jsonObject.put("code","400");
            jsonObject.put("info","token认证失败");
        }
        return jsonObject;
    }
    //注销
    @RequestMapping("/logout")
    @ResponseBody
    public JSONObject logOut(HttpServletRequest request){
        JSONObject jsonObject = new JSONObject();
        HttpSession session = request.getSession();
        if (session == null){

        }else {
            session.invalidate();
            jsonObject.put("info","注销成功");
            jsonObject.put("code", HttpStatus.OK);
        }
        return jsonObject;
    }

子系统需要使用ssoserver中核心的过滤器类,配置此过滤拦截请求

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第13张图片

注册过滤器

@Configuration
public class SSoFilterConfig {

    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setName("ssoFilter");
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("paramName", "paramValue");
        filterRegistrationBean.setFilter(ssoFilter());
        return filterRegistrationBean;
    }


    @Bean
    public SsoFilter ssoFilter(){
        return new SsoFilter();
    }
}

其他子系统配置与此一致

另外

使用springBoot建立能访问jsp的多模块工程还需要注意两个地方的配置,如图:

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第14张图片

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第15张图片

idea基于springboot+redis+springSession+jsp实现简易SSO单点登录_第16张图片

到此sso单点登陆项目搭建完成,代码已上传到码云,地址:https://gitee.com/TangThomas/sso

参考文章:https://www.cnblogs.com/ywlaker/p/6113927.html

                  http://tengj.top/2017/03/13/springboot5/

你可能感兴趣的:(idea基于springboot+redis+springSession+jsp实现简易SSO单点登录)