美文网首页
Experience++

Experience++

作者: LazzMan | 来源:发表于2017-12-01 17:07 被阅读0次

    本文非“原创”,只是遇到问题后谷歌度娘归纳总结。

    1. MySQL中写出一条随机取n条记录的sql文

    答案:

    ① select * from table_name order by rand() limit 0,5;
    ② SELECT * FROM table AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM table)-(SELECT MIN(id) FROM table))+(SELECT MIN(id) FROM table)) AS id) AS t2 WHERE t1.id >= t2.id ORDER BY t1.id LIMIT 1;
    ③ SELECT * FROM table WHERE id >= (SELECT floor( RAND() * ((SELECT MAX(id) FROM table)-(SELECT MIN(id) FROM table)) + (SELECT MIN(id) FROM table))) ORDER BY RAND() LIMIT 1;

    2. 如何实现一个对用户的高并发锁(非分布式)

    答案:

    ① 使用String.inter()是这种思路的一种具体实现。类 String 维护一个字符串池。 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。可见,当String相同时,String.intern()总是返回同一个对象,因此就实现了对同一用户加锁。由于锁的粒度局限于具体用户,使系统获得了最大程度的并发。

    public void doSomeThing(String uid) {  
       synchronized(uid.intern()) {  
           // ...  
       }  
    }
    

    ② String.inter()的缺陷是类 String 维护一个字符串池是放在JVM perm区的,如果用户数特别多,导致放入字符串池的String不可控,有可能导致OOM错误或者过多的Full GC。怎么样能控制锁的个数,同时减小粒度锁呢?直接使用Java ConcurrentHashMap?或者你想加入自己更精细的控制?那么可以借鉴ConcurrentHashMap的方式,将需要加锁的对象分为多个bucket,每个bucket加一个锁,伪代码如下:

    Map locks = new Map();  
    List lockKeys = new List();  
    for(int number : 1 - 10000) {  
       Object lockKey = new Object();  
       lockKeys.add(lockKey);  
        locks.put(lockKey, new Object());  
    }  
      
    public void doSomeThing(String uid) {  
       Object lockKey = lockKeys.get(uid.hash() % lockKeys.size());  
       Object lock = locks.get(lockKey);  
         
       synchronized(lock) {  
          // do something  
       }  
    }  
    

    3. 分布式如何实现一个用户锁

    答案:
    Redis锁

    4. 多请求并发修改资源,如何保证一致性

    答案:
    使用update where 来修改,返回值是0表示修改成功,1则表示失败

    5. 并发添加记录,如何保证数据库不会插入重复数据

    • 使用主键(或联合主键或唯一索引)作为插入约束,保证无法插入重复数据
    • 插入时使用Insert Select Not Exist,避免插入重复数据

    5. java.util.List中有一个subList方法,用来返回一个list的一部分的视图,当修改原有list时,subList返回的视图会跟随变化吗?

    答案:

    List<E> subList(int fromIndex, int toIndex);
    

    它返回原来list的从[fromIndex, toIndex)之间这一部分的视图,之所以说是视图,是因为实际上,返回的list是靠原来的list支持的。

    所以,你对原来的list和返回的list做的“非结构性修改”(non-structural changes),都会影响到彼此对方。

    所谓的“非结构性修改”,是指不涉及到list的大小改变的修改。相反,结构性修改,指改变了list大小的修改。

    那么,如果涉及到结构性修改会怎么样呢?

    如果发生结构性修改的是返回的子list,那么原来的list的大小也会发生变化;

    而如果发生结构性修改的是原来的list(不包括由于返回的子list导致的改变),那么返回的子list语义上将会是undefined。在AbstractList(ArrayList的父类)中,undefined的具体表现形式是抛出一个ConcurrentModificationException。

    因此,如果你在调用了sublist返回了子list之后,如果修改了原list的大小,那么之前产生的子list将会失效,变得不可使用。

    7. MYSQL如何根据条件插入 insert where

    普通的 INSERT INTO 插入:

    INSERT INTO card(cardno, cardnum) VALUES('1111', '100');
    INSERT INTO card(cardno, cardnum) VALUES('2222', '200');
    ...
    

    对于普通的 INSERT 插入,如果想要保证不插入重复记录,我们只有对某个字段创建唯一约束实现(比如:cardno卡号不能重复);

    那有没有不创建唯一约束,仅通过 INSERT INTO 一条语句实现的方案呢?

    答案:有的, INSERT INTO IF EXISTS 具体语法如下:

    INSERT INTO table(field1, field2, fieldn) SELECT 'field1', 'field2', 'fieldn' FROM DUAL WHERE NOT EXISTS(SELECT field FROM table WHERE field = ?)
    

    其中的 DUAL 是一个临时表,不需要物理创建,这么用即可。

    针对上面的card示例的改造如下:

    INSERT INTO card(cardno, cardnum) SELECT '111', '100' FROM DUAL WHERE NOT EXISTS(SELECT cardno FROM card WHERE cardno = '111');
    INSERT INTO card(cardno, cardnum) SELECT '222', '200' FROM DUAL WHERE NOT EXISTS(SELECT cardno FROM card WHERE cardno = '222');
    

    8. 解释key-value与name-value的区别

    key-value 一般指dict或map中的键值对。

    name-value 一般指单条的键值,例如cookie记录,每条记录就是name-value形式,以及redis中的记录都是采用这种名称定义。

    9. 介绍setTimeout的原理

    setTimeout是异步任务,类似纤程,如果主线程被阻塞,setTimeout也会被阻塞。
    javascript真的是异步的吗

    10. 避免前端快速点击发送多个请求

    使用lodash的节流或防抖函数debounce,throttle

    11. H5中针对所有点击事件设置过滤器(用于判断全局事件:例如无网络禁止点击触发)

    
    ## 某个组件内绑定点击事件
    import Interceptors from '../common/interceptors.js'
    ...
    <my-component onClick={Interceptors.clickInterceptor(()=>{
      window.fetch('http://xxxx/xxx.json');
    })}/>
    
    ## 定义全局的方法 interceptors.js
    export default {
      clickInterceptor(cb){
        // 判断有无网络
        if (!navigator.onLine) {
          window.alert('没网啥都干不了!!!');
          return;
        }
        // XXX判断
        ...
        // 执行回调
        cb();
      }
    }
    

    12. React开发时,遇到安卓4.4不兼容问题(按钮无法点击/页面报错)

    • 检查是否引入babel-pollyfill
    • 检查chrome30上样式是否正常,可能会因为遮罩导致按钮无法点击

    13. Provisional headers are shown原因

    • 如果发生在表单提交时,注意form的action与ajax只能保留一个。
    • 出现在载入缓存资源,请求没有被发送, 而如果上一个资源加载失败,可能导致从缓存加载的资源失败,
      即缓存资源请求之前的请求不能失败,不然就有可能出现问题

    14. form下面的buttoninput type="button"有什么区别

    • <input type="button" /> 这就是一个按钮。如果你不写javascript 的话,按下去什么也不会发生。
    • <input type="submit" /> 这样的按钮用户点击之后会自动提交 form,除非你写了javascript 阻止它。
    • <button> 这个按钮放在 form 中也会点击自动提交,比前两个的优点是按钮的内容不光可以有文字,还可以有图片等多媒体内容。(当然,前两个用图片背景也可以做到)。它的缺点是不同的浏览器得到的 value 值不同; 其他标签,例如 a, img, span, div,然后用图片把它伪装成一个按钮。

    15. 安全的认证方式

    安全的校验客户身份:
    客户端登录参数:account md5(account+password)
    服务端验证:使用同样的算法计算md5结果,如果相同则说明登录成功。
    该方式可保证信息传输中安全,即使遭遇中间人攻击也无法获取到账号密码。

    防止重放攻击(中间人拦截封包后重复发包):
    客户端请求参数:time=当前时间戳 sign=MD5(token+time)
    服务端验证: 计算MD5(token+time)与sign结果是否一致,一致则返回结果,并标记time失效(可以在服务端用户会话中保存上一个sign,基本上相邻的两次请求sign一定不相同),另外time与服务端差距过大也会被拒绝(用户会话超时)。
    该方式可保证可保证信息传输中安全,中间人无法重复发送同一封包。

    16. JWT(JSON Web Token)的使用场景

    在我看来,JWT的使用场景很有限:需要服务端无状态,使用JWT则是一种选择;一次性业务操作,可以使用JWT;跨系统信息交换,可以使用JWT

    简单的介绍下JWT组成原理:
    JWT生成的token分为三部分:Base64(header).Base64(payload).sign

    • header一般是这样的:
    {
      "typ": "JWT",
      "alg": "HS256" # 或者HMAC,都是密钥散列算法(签名sign = hmac(自定义密钥secret + 原文))
    }
    

    在经过base64编码后值为:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

    • payload一般是这样的:
    {
        "iss": "John Wu JWT",
        "iat": 1441593502,
        "exp": 1441594722,
        "aud": "www.example.com",
        "sub": "jrocket@example.com",
        "from_user": "B",
        "target_user": "A"
    }
    

    这里面的前五个字段都是由JWT的标准所定义的。
    iss: 该JWT的签发者
    sub: 该JWT所面向的用户
    aud: 接收该JWT的一方
    exp(expires): 什么时候过期,这里是一个Unix时间戳
    iat(issued at): 在什么时候签发的

    将上面的JSON对象进行[base64编码]可以得到下面的字符串。eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
    这个字符串我们将它称作JWT的Payload(载荷)。

    • signature签名

    将上面的两个编码后的字符串都用句号.连接在一起(头部在前),就形成了eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
    最后,我们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,我们还需要提供一个密钥(secret)。如果我们用mystar作为密钥的话,那么就可以得到我们加密后的内容:rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
    这一部分又叫做签名。

    最后将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWTeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

    JWT总结:只有服务端知道密钥,因此服务端签名的信息无法被篡改;payload中可以存放一些不敏感的用户数据,由于payload本身仅仅是经过base64编码,所以只要经过base64解码就可以看到payload中的数据。因此在我看来JWT非常适用于后台服务无状态的场景,可以将用户的权限以及用户身份放到payload中,这样后端就不需要维持用户会话(session),即可实现无状态。由于JWT标准比较成熟,跨系统数据交换时可以考虑使用JWT,比如A系统需要传递用户的id给B系统,A系统生成jwt令牌给前端,前端传给B系统,B系统使用与A系统约定好的密钥验证令牌,有效则认为消息合法,也要注意不要使用jwt传递敏感信息,payload不是安全的。
    JWT弊端:JWT 只是签名过的 cookie,一个有实现规范的协议而已,事实上就是一个加密的 cookie,保存信息容量有限(cookie最大4K),无法解决服务端过期的实现(可以服务端记录 JWT id 和 timestamp 做到,不过这样不是又回到服务器 session 的实现了么?)别再使用JWT做会话管理了

    17. 前端网页安全

    1. 防止页面点击劫持

    使用一个HTTP头——X-Frame-Options。X-Frame-Options可以说是为了解决ClickJacking而生的,它有三个可选的值:
    DENY:浏览器会拒绝当前页面加载任何frame页面;
    SAMEORIGIN:frame页面的地址只能为同源域名下的页面;
    ALLOW-FROM origin:允许frame加载的页面地址;
    PS:浏览器支持情况:IE8+、Opera10+、Safari4+、Chrome4.1.249.1042+、Firefox3.6.9。

    NGINX 添加如下配置:
    add_header X-Frame-Options SAMEORIGIN;

    1. 防御CSRF攻击

    a.重要数据交互采用POST进行接收,当然是用POST也不是万能的,伪造一个form表单即可破解
    b.使用验证码,只要是涉及到数据交互就先进行验证码验证,这个方法可以完全解决CSRF。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案。
    c.验证HTTP Referer字段,该字段记录了此次HTTP请求的来源地址,最常见的应用是图片防盗链。百度:nginx利用referer指令实现防盗链配置。
    d.为每个表单添加令牌token并验证。大了说就是重要业务不仅要使用POST提交,并且在开始业务前生成一个一次性令牌(crsf_token),提交业务时传递令牌,后端校验令牌,这样攻击者无法构造出令牌即无法实现攻击。

    1. 防御xss攻击

    a.网页编码使用UTF-8,防止GB系列编码宽字节注入
    b.尽量使用前后端分离方式开发网页,尽量不要使用jsp形式或者其他模板形式
    c.前端尽量使用成熟框架vue或react,框架自身实现了xss过滤,建议不要使用v-html这种形式渲染页面(官方也不建议,因此这种方式可能会遇到xss注入)

    相关文章

      网友评论

          本文标题:Experience++

          本文链接:https://www.haomeiwen.com/subject/fanmbxtx.html