自定义拦截器

用于拦截请求,一般作用与单点登录系统。

创建拦截器

创建拦截器,必须实现接口HandlerInterceptor,并重写三个方法,添加业务

访问所有Controller中的任何方法,都会触发拦截器,执行拦截器的功能

package cn.tedu.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//自定义拦截器:在方法执行前后添加与业务无关的功能
@Component//交给spring管理bean的ioc di
public class MyInterceptor implements HandlerInterceptor{
    long start ;
    long end ;
    @Override //在方法执行前调用此处一般用于sso获取token。并验证如果存在则true,不存在重新登陆
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        start = System.currentTimeMillis();
        System.out.println("方法要被执行啦~");
        return true;//放行
    }
    @Override //在方法执行后调用
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        end = System.currentTimeMillis();
        System.out.println("方法执行完啦~");
    }
    @Override //都完成后调用
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("都干完啦~耗时: "+(end-start)+"ms");
    }
}

注册拦截器

注册拦截器,必须实现接口WebMvcConfigure,通知spring容器管理这个类

package cn.tedu.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration//告诉spring容器,我是一个配置类
public class RegistInterceptor implements WebMvcConfigurer {

    @Autowired//自动装配
    private MyInterceptor a ;


    /**
     * 添加拦截器-进行拦截
     * addPathPatterns 添加拦截
     * excludePathPatterns 排除拦截
     **/
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(a)
                .addPathPatterns("/**")
                //====== 以下为不需要拦截的地址 ======
                //跳转到登录页
                .excludePathPatterns("/login")
                //跳转到登出成功页
                .excludePathPatterns("/logout")
                //统一登录
                .excludePathPatterns("/sso/login")
                //统一登出
                .excludePathPatterns("/sso/logOut")
                //校验登录开放接口
                .excludePathPatterns("/sso/checkLogin")
                //校验令牌开放接口
                .excludePathPatterns("/sso/checkToken");
        super.addInterceptors(registry);
    }

使用拦截器

打开浏览器,访问所有Controller中的任何方法,都会触发拦截器,执行拦截器的功能

package cn.tedu.interceptor;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/in")
public class InController {

    @RequestMapping("/login")
    public void get(){
        System.out.println(111);
    }
    @RequestMapping("/save")
    public void save(){
        for (int i = 0; i < 1000; i++) {
            System.out.print(i);
        }
    }
}

测试

 sso登录接口


/**
 * SSO 登录相关接口
 *
 * @author 
 */
@Slf4j
@Controller
@RequestMapping("/sso")
public class SSOLoginController {


    /**
     * 认证中心SSO统一登录方法
     */
    @RequestMapping("/login")
    public String login(LoginParam loginParam, RedirectAttributes redirectAttributes,
                        HttpSession session, Model model) {

        //Demo 项目此处模拟数据库账密校验
        if (!"admin".equals(loginParam.getUsername()) || !"123456".equals(loginParam.getPassword())) {
            model.addAttribute("msg", "账户或密码错误,请重新登录!");
            model.addAttribute("redirectUrl", loginParam.getRedirectUrl());
            return "login";
        }

        //登录成功
        //创建令牌
        String ssoToken = UUID.randomUUID().toString();
        //把令牌放到全局会话中
        session.setAttribute("ssoToken", ssoToken);
        //设置session失效时间-单位秒
        session.setMaxInactiveInterval(3600);
        //将有效的令牌-放到map容器中(存在该容器中的token都是合法的,正式环境建议存库或者redis)--------------------------------------
        SSOConstantPool.TOKEN_POOL.add(ssoToken);

        //未携带重定向跳转地址-默认跳转到认证中心首页
        if (StringUtils.isEmpty(loginParam.getRedirectUrl())) {
            return "index";
        }

        // 携带令牌到客户端
        redirectAttributes.addAttribute("ssoToken", ssoToken);
        log.info("[ SSO登录 ] login success ssoToken:{} , sessionId:{}", ssoToken, session.getId());
        // 跳转到客户端
        return "redirect:" + loginParam.getRedirectUrl();
    }

    /**
     * 校验令牌是否合法
     *
     * @param ssoToken    令牌
     * @param loginOutUrl 退出登录访问地址
     * @param jsessionid
     * @return 令牌是否有效
     */
    @ResponseBody
    @RequestMapping("/checkToken")
    public String verify(String ssoToken, String loginOutUrl, String jsessionid) {
        // 判断token是否存在map容器中,如果存在则代表合法
        boolean isVerify = SSOConstantPool.TOKEN_POOL.contains(ssoToken);
        if (!isVerify) {
            log.info("[ SSO-令牌校验 ] checkToken 令牌已失效 ssoToken:{}", ssoToken);
            return "false";
        }

        //把客户端的登出地址记录起来,后面注销的时候需要根据使用(生产环境建议存库或者redis)
        List<ClientRegisterModel> clientInfoList =
                SSOConstantPool.CLIENT_REGISTER_POOL.computeIfAbsent(ssoToken, k -> new ArrayList<>());
        ClientRegisterModel vo = new ClientRegisterModel();
        vo.setLoginOutUrl(loginOutUrl);
        vo.setJsessionid(jsessionid);
        clientInfoList.add(vo);
        log.info("[ SSO-令牌校验 ] checkToken success ssoToken:{} , clientInfoList:{}", ssoToken, clientInfoList);
        return "true";
    }

    /**
     * 校验是否已经登录认证中心(是否有全局会话)
     * 1.若存在则携带令牌ssoToken跳转至目标页面
     * 2.若不存在则跳转到登录页面
     */
    @RequestMapping("/checkLogin")
    public String checkLogin(String redirectUrl, RedirectAttributes redirectAttributes,
                             Model model, HttpServletRequest request) {
        //从认证中心-session中判断是否已经登录过(判断是否有全局会话)
        Object ssoToken = request.getSession().getAttribute("ssoToken");

        // ssoToken为空 - 没有全局回话
        if (StringUtils.isEmpty(ssoToken)) {
            log.info("[ SSO-登录校验 ] checkLogin fail 没有全局回话 ssoToken:{}", ssoToken);
            //登录成功需要跳转的地址继续传递
            model.addAttribute("redirectUrl", redirectUrl);
            //跳转到统一登录页面
            return "login";
        }

        log.info("[ SSO-登录校验 ] checkLogin success 有全局回话  ssoToken:{}", ssoToken);
        //重定向参数拼接(将会在url中拼接)
        redirectAttributes.addAttribute("ssoToken", ssoToken);
        //重定向到目标系统
        return "redirect:" + redirectUrl;
    }

    /**
     * 统一注销
     * 1.注销全局会话
     * 2.通过监听全局会话session时效性,向已经注册的所有子系统发起注销请求
     */
    @RequestMapping("/logOut")
    public String logOut(HttpServletRequest request) {
        HttpSession session = request.getSession();
        log.info("[ SSO-统一退出 ] ....start.... sessionId:{}", session.getId());
        //注销全局会话, SSOSessionListener 监听器会处理后续操作
        request.getSession().invalidate();
        log.info("[ SSO-统一退出 ] ....end.... sessionId:{}", session.getId());
        return "logout";
    }
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值