zoukankan      html  css  js  c++  java
  • 前后端分离[转载] --JAVA框架-前后端分离(跨域和JWT)

    JAVA框架-前后端分离(跨域和JWT)

     

    跨域

    当我们在做前后端分离项目的时候,Tomcat往往并不是将前端页面和后端程序统一部署的,一般我们会有一个单独的部署静态html的服务器,那么此时前端的服务器如果想要访问后端的服务器时候,浏览器默认是会拦截这个操作的,因为浏览器有一种叫做同源策略(Same origin policy)的安全机制。

    同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

    同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,浏览器会先发送OPTION请求进行预检查,判断服务器是否允许跨域,如果允许才发送真正的请求,否则抛出异常。

    那么,我们总结一下什么叫做跨域?

    • 当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。

    什么时候回产生跨域问题呢?

    • 浏览器在解析执行一个网页时,如果页面中的js代码请求了另一个非同源的资源,则会产生跨越问题,而浏览器直接跳转另一个非同源的地址时不会有跨域问题

    如何解决跨域问题呢?

    既然禁止跨域问题时浏览器的行为,那么只需要设置浏览器运行解析跨域请求的数据即可,但是这个设置必须放在服务器端,由服务器端来判断对方是否可信任,在响应头中添加一个字段,告诉浏览器,某个服务器是可信的。

    复制代码
    public class AServlet extends javax.servlet.http.HttpServlet {
        protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
        }
    
        protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
            //设置允许跨域,其中*表示允许所有地址访问
            response.setHeader("Access-Control-Allow-Origin","*");
            String s = "{"name":"jack"}";
            response.getWriter().println(s);
        }
    }
    

    还有一些其他的相关设置

    复制代码
    //指定允许其他域名访问
    'Access-Control-Allow-Origin:http://XXX.XXX.XXX'//一般用法(*,指定域,动态设置),注意*不允许携带认证头和cookies
    //预检查间隔时间
    'Access-Control-Max-Age: 1800'
    //允许的请求类型
    'Access-Control-Allow-Methods:GET,POST,PUT,POST'
    //列出必须携带的字段
    'Access-Control-Allow-Headers:x-requested-with,content-type'
    

    状态保持问题和JWT

    在传统项目中,我们把登录后的用户信息一般存放在cookie和session中,但这在前后端分离项目中出现了问题。sessionid是使用cookie存储在客户端的,而cookie遵守同源策略,只在同源的请求中有效。虽然我们可以在cookie中指定domain来解决,但是cookie必须针对性的设置作用域,这对于有多个不同域要共享cookie时,可操作性差,难以维护。

    另外在分布式架构中,共享session和cookie也是一大问题,必须引入第三方来完成session的存储和共享(也可通过中间层做cookie转发如Nginx,Node.js),这也是传统单体服务无法支持分布式和集群的问题所在。

    为了解决以上问题,我们需要一种新的状态保持方法:JWT(json WEB token)

    JWT的工作流程

    回顾,之所以使用session和cookie是因为HTTP的无状态性质,导致服务器无法识别多次请求是否来自同一个用户。

    JWT可以对用户信息进行加密生成一个字符串,下发到客户端,客户端在后续请求中携带该字符串,服务器解析后取出用户信息,从而完成用户身份的识别,如下图:

    简而言之,JWT就是一串加密后的字符串,长这个样子:

    中间由“.”分割成三部分,

    • Header(头部)
      Header 部分是一个 JSON 对象,描述 JWT 的元数据,例如签名算法等,像下面这样:

      复制代码
      {
        "alg": "HS256",
        "typ": "JWT"
      }
      

      alg属性表示签名的算法,默认是 HMAC SHA256;
      typ属性表示这个令牌(token)的类型统一写为JWT

    最后使用base64URL算法转换为字符串;

    • Payload(负载)
      Payload 部分也是一个 JSON 对象,用来存放真正需要传递的数据,JWT 规定了7个保留字段,如下:

      复制代码
        iss (issuer):签发人
        exp (expiration time):过期时间
        sub (subject):主题
        aud (audience):受众
        nbf (Not Before):生效时间
        iat (Issued At):签发时间
        jti (JWT ID):编号
      
    • Signature(签名)
      部分是对前两部分的签名,防止数据篡改。签名时需要指定一个密钥(secret)。密钥只有服务器才知道,不能泄露给用户。然后使用 Header 里面指定的签名算法(默认是 HMAC SHA256)

    JWT的优点和缺点?

    优点

    • 满足REST Full的无状态要求(为了提高系统的扩展性,REST要求所有信息由请求端来提供,如此才使得JWT成为了分布式,集群构架的首选方式)

    • 在分布式,集群系统,前后端分离中使身份验证变得非常简单

    • 可用于其他数据交换

    • 合理的使用可减少数据库查询次数

    缺点:

    • 对于同样的数据JWT整体大小超过cookie,这会增加网络开销

    • 服务器每次解析JWT都需要再次执行对应的算法,这将增加系统开销

    • 在传统单体服务,和WEBApp形式的前后端分离项目中使用JWT反而不如Session+cookie

    简单的JWT案例

    首先我们要导入几个jar包

    复制代码
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTCreator;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.fasterxml.jackson.databind.util.TokenBuffer;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Date;
    
    @WebServlet(name = "Servlet" ,urlPatterns = "/test")
    public class Servlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String name = request.getParameter("name");
            String pwd = request.getParameter("pwd");
    
    
            response.setContentType("application/json;charset=utf-8");
    
            if(name.equals("jeason") && pwd.equals("123") ){
                //生成一个Token,返回给客户端
                JWTCreator.Builder builder = JWT.create();
    
                //指定算法,传入一个秘钥
                Algorithm algorithm = Algorithm.HMAC256("2341234SKDJFALSJFLKAJSDKJ");
                //支持链式调用
                String Token = builder
                                    .withSubject("test this jwt")//设置主题
                                    .withExpiresAt(new Date(new Date().getTime() + (1000 * 60 * 30)))//设置时间
                                    .withClaim("userID", "No.001")//负载数据(用户自定义)
                                    .sign(algorithm);
                System.out.println("Token is:"+ Token);
    
                //将Token放入响应头
                response.setHeader("Token",Token);
                response.getWriter().println("登陆成功");
            }else {
                response.getWriter().println("登录失败");
            }
    
        }
    }
    
    

    这样,我们就简单的实现了,登录后由服务器给客户端发送一个标记信息的Token。

    另外,我们还可以写一个简单的例子来检查收到的请求是否包含正确的Token:

    复制代码
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.exceptions.JWTVerificationException;
    import com.auth0.jwt.interfaces.DecodedJWT;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet(name = "CheckServlet",urlPatterns = "/check")
    public class CheckServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //取出Token
            String token = request.getHeader("Token");
            if(token != null){
                //检查Token是否有效,主要看是否过期,以及是否被篡改
                //指定算法,传入一个秘钥
                Algorithm algorithm = Algorithm.HMAC256("2341234SKDJFALSJFLKAJSDKJ");
                JWTVerifier verifier = JWT.require(algorithm).build();
                try{
                    verifier.verify(token);
    
                    DecodedJWT decode = JWT.decode(token);
                    String userID = decode.getClaim("userID").asString();
                    System.out.println(userID);
    
                    response.getWriter().println("Token 验证成功!!");
                }catch ( JWTVerificationException e ){
                    System.out.println("Token 过期或者被篡改!!");
                    response.getWriter().println("Token 过期或者被篡改!!");
                }
    
    
    
            }else{
                System.out.println("NO  token");
            }
        }
    }




    转载自博客: https://www.cnblogs.com/JeasonIsCoding/p/13232663.html
  • 相关阅读:
    实验11——指针的基础应用
    C语言程序设计第10堂作业
    实验九——基本数据类型存储及应用总结
    C语言程序设计第8堂作业
    实验七——函数定义及调用总结
    实验六——循环结构程序练习总结
    实验五——循环结构学习总结
    实验四——多分支结构及本章总结
    9.29第三次作业
    zuoyeQAQ
  • 原文地址:https://www.cnblogs.com/zfcsharp/p/13906174.html
Copyright © 2011-2022 走看看