【无标题】

Redis存储会话
  • 登录/注册 调用
redisOperator.set("userToken:" + users.getId(), token);
CookieUtils.setCookie(request,response,"user",JsonUtils.objectToJson(usersVo),true);
users.setToken(token);
  • 退出登录 调用 前端删除cookie
redisOperator.del("userToken:" + userId);
SpringSession实现会话管理
  • 引入依赖
<!--  spring-session依赖   -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • yml 文件配置
spring:
  session:
    store-type: redis
  • 开启
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) //否则强制登录
@MapperScan(basePackages = "com.njzy.mapper")扫码mybatis mapper
@EnableScheduling // 开启定时任务
@EnableRedisHttpSession //开启使用Redis作为SpringSession
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
拦截器配置
  • 拦截器的类需要实现HandlerInterceptor
import com.njzy.result.Result;
import com.njzy.utils.JsonUtils;
import com.njzy.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

public class UserInterceptor implements HandlerInterceptor {

    @Autowired
    RedisOperator redisOperator;

    /**
     * 在controller调用之前拦截 在此拦截
     *
     * @return false: 请求被拦截 验证出现问题     true 校验通过
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("token");
        String userId = request.getHeader("userId");
        if (StringUtils.isBlank(token) || StringUtils.isBlank(userId)) {
            returnErrorResponse(response, Result.error("验证失败"));
            return false;
        }
        String value = redisOperator.get("userToken:" + userId);
        if (!StringUtils.isBlank(value)) {
            if (token.equals(value)) {
                returnErrorResponse(response, Result.error("验证成功"));
            } else {
                returnErrorResponse(response, Result.error("token不匹配,其他客户端登录"));
                return false;
            }
        }
        returnErrorResponse(response, Result.error("验证失败"));
        return true;
    }

    private void returnErrorResponse(HttpServletResponse response, Result result) {
        OutputStream out = null;
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/json");
            out = response.getOutputStream();
            out.write(JsonUtils.objectToJson(result).getBytes("utf-8"));
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 在controller后 渲染视图前
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 在controller后 渲染视图后
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
  • 注册拦截器
import com.njzy.interceptor.UserInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");

        // 配置knife4j 显示文档
        registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        //配置swagger-ui显示文档
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");//swagger 映射 一旦配置必须设置
        //公共文件
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/")
                .addResourceLocations("file:/Users/yuanfang/Downloads/images/");//本地路径资源地址
    }

    @Bean
    public UserInterceptor userInterceptor() {
        return new UserInterceptor();
    }

    /**
     * 注册拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userInterceptor())
                .addPathPatterns("/**")//匹配所有路径
                .excludePathPatterns("/doc.html/**", "/swagger-resources", "/v3/api-docs",// 排除接口文档相关拦截
                        "/swagger-ui.html/**", "/webjars/**", "/error",
                        "/user/checkUsername", "/user/register", "/user/login");//排除不需要登录的拦截
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}
CAS 系统实现单点登录
  • 场景:多套独立的系统使用一套用户体系,在一个系统上登录后,其他系统无需登录,一个系统注销,其他系统需要重新登录
  • 由多系统和cas系统组成
    在这里插入图片描述
  • 单点登录controller代码
import com.njzy.pojo.vo.UsersVo;
import com.njzy.pojo.bo.UsersBo;
import com.njzy.result.Result;
import com.njzy.service.UsersService;
import com.njzy.utils.CookieUtils;
import com.njzy.utils.JsonUtils;
import com.njzy.utils.MD5Util;
import com.njzy.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Controller // 需要返回页面
public class SSOController {
    @Autowired
    UsersService usersService;

    @Autowired
    RedisOperator redisOperator;

    //cookie的key值 存放的是全局的ticket 一般需要加密处理
    private final String COOK_NAME = "cookie_user_ticket";

    //存放分布式会话的key前缀 拼接用户id组成key  里面存放用户信息
    private final String USER_TOKEN = "userToken";

    //key 前缀 + 全局门票  value 里面存放用户ID
    private final String USER_TICKET = "userTicket";


    @GetMapping("/login")
    public String sso(String returnUrl, Model model, HttpServletRequest request, HttpServletResponse response) {
        model.addAttribute("returnUrl", returnUrl);
        //获取ticket 如果cookie中有说明用户登录过 此时发放一次tempTicket
        String ticket = CookieUtils.getCookieValue(request, COOK_NAME);
        if (verifyTicket(ticket)) {
            String tempTicket = UUID.randomUUID().toString().trim();
            redisOperator.set("useTempTicket:" + tempTicket, MD5Util.md5(tempTicket), 600);
            return "redirect:" + returnUrl + "?tempTicket=" + tempTicket;
        }
        //用户未登录 统一跳转CAS登录页面
        return "login";
    }

    /**
     * 校验全局ticket
     */
    private boolean verifyTicket(String ticket) {
        if (StringUtils.isBlank(ticket)) {
            return false;
        }
        // 验证 cas门票是否有效
        String userId = redisOperator.get(USER_TICKET + ":" + ticket);
        if (StringUtils.isBlank(userId)) {
            return false;
        }
        // 验证会话是否存在
        String s = redisOperator.get(USER_TOKEN + ":" + userId);
        if (StringUtils.isBlank(s)) {
            return false;
        }
        return true;
    }

    /**
     * CAS 统一登录接口
     * 创建用户全局会话 token
     * 创建用户全局门票 用于表示在cas端是否登录 ticket
     * 创建用户临时票据 用于回跳回传 一次性 tempTicket
     */
    @PostMapping("/doLogin")
    public String doLogin(String username, String password, String returnUrl, Model model, HttpServletRequest request, HttpServletResponse response) {
        model.addAttribute("returnUrl", returnUrl);
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            model.addAttribute("errmsg", "用户名密码不能为空");
            return "login";
        } else {
            UsersBo usersBo = new UsersBo();
            usersBo.setUsername(username);
            usersBo.setPassword(password);
            // 1.登录
            UsersVo result = usersService.doLogin(usersBo, request, response);

            if (result != null) {
                //2.通过redis实现用户会话
                String token = UUID.randomUUID().toString().trim();
                UsersVo usersVo = new UsersVo();
                BeanUtils.copyProperties(result, usersVo);
                usersVo.setToken(token);
                redisOperator.set(USER_TOKEN + ":" + result.getId(), JsonUtils.objectToJson(usersVo));
            } else {
                model.addAttribute("errmsg", "用户名密码不正确");
                return "login";
            }
            // 3。生成全局ticket门票 并放在CAS端的cookie中
            String ticket = UUID.randomUUID().toString().trim();
            CookieUtils.setCookie(request, response, COOK_NAME, ticket);

            // 4、全局ticket关联用户id 并存放到redis
            redisOperator.set(USER_TICKET + ":" + ticket, result.getId());

            // 5、生成临时ticket 回跳前端网址
            String tempTicket = UUID.randomUUID().toString().trim();
            redisOperator.set("useTempTicket:" + tempTicket, MD5Util.md5(tempTicket), 600);
            return "redirect:" + returnUrl + "?tempTicket=" + tempTicket;
        }
    }

    @PostMapping("/verifyTmpTicket")
    @ResponseBody
    public Result verifyTmpTicket(String tmpTicket, HttpServletRequest request, HttpServletResponse response) {
        //校验用户是否登录
        String tmpTicketValue = redisOperator.get("useTempTicket:" + tmpTicket);
        if (StringUtils.isBlank(tmpTicketValue)) {
            return Result.error(("用户票据错误"));
        }
        if (!tmpTicketValue.equals(MD5Util.md5(tmpTicket))) {
            return Result.error(("用户票据错误"));
        } else {
            //校验成功 后需要销毁票据
            redisOperator.del("useTempTicket:" + tmpTicket);
        }
        return Result.success();
    }

    @PostMapping("/logout")
    @ResponseBody
    public Result logout(String userId, HttpServletRequest request, HttpServletResponse response) {
        String ticket = CookieUtils.getCookieValue(request, COOK_NAME);
        // 删除 cookie和redis中的票据
        CookieUtils.deleteCookie(request, response, COOK_NAME);
        redisOperator.del(USER_TICKET + ":" + ticket);
        //清除会话信息
        redisOperator.del(USER_TOKEN + ":" + userId);
        return Result.success();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值