单点登录和jwt的引入和操作

单点登录

==============================================================================
单点登录三种常见的方式

已知每个微服务都有自己的tomcat,session是不共享的,为了解决多模块共享数据和登录问题

  1. session广播 :sessoin复制 淘汰
  2. 使用cookie+redis实现 :cookie客户版技术,每次发送请求,带着cookie发送,redis基于key-vaule存储
  3. 使用token:按照一定规则生成字符串,字符串包含用户信息

==============================================================================

使用cookie+redis实现

在项目中任何一个模块,登陆后,把数据放到2个地方

步骤

1.rediskey:生成唯一随机值(IP,用户等信息) ,value:用户数据
2. cookie:吧redis里面生成的key值放到cookie
3. 当其他模块访问,发送带cookie的请求时,获取cookie值,拿cookie值去redis中查询,也就是key,查到就可以返回正常数据,没有就是null

构建

		<dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

yml

平时redis怎么设置端口和ip就怎么设置

操作

当你使用 HttpSession
session中的session.setAttribute();就可以实现redis存储,读取也是从session.getAttribute()`中读取

==============================================================================

使用token

  1. 给登陆服务器留下登录痕迹
  2. 登录服务器要将token放到重定向url地址上
  3. 其他系统要处理url地址上的关键token,只要有,则将token对应的用户保存到自己的分布式session中
  4. 自己的系统将用户保存在自己的会话中

demo测试

单点登录服务器:
@Controller
public class loginController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 根据token去redis中获取用户信息
     * @param token
     * @return
     */
    @ResponseBody
    @GetMapping("/info")
    public String getUserInfo(@RequestParam("token")String token){
        String s = redisTemplate.opsForValue().get(token);
        return s;
    }
    /**
     * 感知登录功能
     * @param url 客户端的地址
     * @param model
     * @param sso_token 
     * @return
     */
    @GetMapping("/login.html")
    public String loginPage(@RequestParam("redirect_url") String url,
                            Model model,
                            @CookieValue(value = "sso_token",required = false)String sso_token) {
        if(!StringUtils.isEmpty(sso_token)){
            //说明之前有人登录过,留下痕迹
            return "redirect:" + url+"?token="+sso_token;
        }
        model.addAttribute("url", url);
        return "login";
    }
    /**
     * 
     * @param username
     * @param password
     * @param url 客户端的地址
     * @param response 设置cookie信息,做感知准备
     * @return
     */
    @PostMapping("/doLogin")
    public String doLogin(@RequestParam("username") String username,
                          @RequestParam("password") String password,
                          @RequestParam("url") String url,
                          HttpServletResponse response) {
        //模拟登录成功
        if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
            //user save
            String uuid = UUID.randomUUID().toString().replace("-", "");
            //redis中存放token
            redisTemplate.opsForValue().set(uuid,username);
            //当前域名下的cookie存放token信息给@GetMapping("/login.html")做感知登录,
            //1. 给登陆服务器留下登录痕迹
            response.addCookie(new Cookie("sso_token", uuid));
            //登录成功跳转到之前的页面
            //2. 登录服务器要将token放到重定向url地址上
            return "redirect:" + url+"?token="+uuid;
        }
        return "login";
    }
}


多系统客户端模拟:
@Controller
public class hellocontroller {
    @Value("${sso.server.url}")
    private String ssoServerUrl;
    /**
     * 测试无需登录
     * @return
     */
    @ResponseBody
    @GetMapping("/hello")
    public  String  hello(){
        return "hello";
    }
    /**
     * 客户端
     * @param model
     * @param session
     * @param token
     * @return
     */
    @GetMapping("/employees")
    public String employees(Model model,
                            HttpSession session,
                            @RequestParam(value = "token",required = false) String token){
        //3. 客户系统要处理url地址上的关键token,只要有,则将token对应的用户保存到自己的分布式session中
        if(!StringUtils.isEmpty(token)){
            RestTemplate restTemplate = new RestTemplate();
            //发送请求给单点登陆服务器获取用户信息
            ResponseEntity<String> forEntity = restTemplate.getForEntity("http://sso.com:8080/info?token=" + token, String.class);
            String body = forEntity.getBody();
            //4. 自己的系统将用户保存在自己的会话中
            session.setAttribute("loginUser",body);
        }
        Object user = session.getAttribute("loginUser");
        if(user==null){
            //没有登录,跳转到登录服务器登录
            return "redirect:"+ssoServerUrl+"?redirect_url=http://client1.com:8081/employees";
        }
        //模拟渲染页面数据
        /**
         *<!DOCTYPE html>
         * <html lang="en" xmlns:th="http://www.thymeleaf.org">
         * <head>
         *     <meta charset="UTF-8">
         *     <title>Title</title>
         * </head>
         * <body>
         *     <h1>欢迎:[[${session.loginUser}]]</h1>
         *     <ul>
         *         <li th:each="emp : ${emps}">name :[[${emp}]]</li>
         *     </ul>
         * </body>
         * </html>
         */
        List<String> emps =new ArrayList<>();
        emps.add("aa");
        emps.add("bb");
        emps.add("cc");
        model.addAttribute("emps", emps);
        return "list";
    }
}

==============================================================================

引入JWT

项目模块进行登录后,按照一定规则把用户信息包含到字符串中,把字符串返回。

  1. 可以通过cookie返回
  2. 可以通过地址栏返回 http://ip:port/xxxx?token=xxxxxx形式
  3. 再去访问其他模块,取到之前生成的token字符串,通过一定的算法,解析,获取到用户信息,即可登录
    jwt是给我们规定好了规则,使用jwt规则生成字符串,包含用户信息,也可以自己写个算法

构建

 		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>

创建工具类

public class JwtUtils {

    //设置token过期时间
    public static final long EXPIRE = 1000 * 60 * 60 * 24;
    //密钥随便添,或者每个公司项目的密钥自己设置
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
    //生成token字符串方法
    public static String getJwtToken(String id, String nickname){

        String JwtToken = Jwts.builder()
                //设置jwt的头信息 不用改
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                .setSubject("my_project_user")//分类 按照自己的项目
                .setIssuedAt(new Date())//设置token过期时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                
                .claim("id", id) //设置token主题部分,存储用户信息
                .claim("nickname", nickname)
                
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token获取会员id
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }
}

进阶:

https://blog.csdn.net/weixin_45031570/article/details/124880833?spm=1001.2014.3001.5502

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值