先来看下 Spring Security密码登录大概流程,模拟这个流程,开发短信登录流程

1,密码登录请求发送给过滤器 UsernamePasswordAuthenticationFilter

2,过滤器拿出用户名密码组装成 UsernamePasswordAuthenticationToken 对象传给AuthenticationManager

3,AuthenticationManager 会从一堆 AuthenticationProvider 里选出一个Provider 处理认证请求。挑选的依据是AuthenticationProvider 里有个

boolean supports(Class<?> authentication);方法,判断当前的provider是否支持传进的token,如果支持就用这个provider认证这个token,并调用authenticate() 方法 进行认证

4,认证过程会调用UserDetailsService获取用户信息,跟传进来的登录信息做比对。认证通过会把UsernamePasswordAuthenticationToken做一个标识   标记已认证,放进session。

做短信登录,不能在这个流程上改,这是两种不同的登录方式,混在一起代码质量不好,需要仿照这个流程写一套自己的流程:

SmsAuthenticationFilter:拦截短信登录请求,从请求中获取手机号,封装成 SmsAuthenticationToken 也会传给AuthenticationManager,AuthenticationManager会找适合的provider,自定义SmsAuthenticationProvider校验SmsAuthenticationToken 里手机号信息。也会调UserDetailsService 看是否能登录,能的话标记为已登录。

其中SmsAuthenticationFilter 参考UsernamePasswordAuthenticationFilter写,SmsCodeAuthenticationToken参考UsernamePasswordAuthenticationToken写,其实就是就是复制粘贴改改

从上图可知,需要写三个类:

1,SmsAuthenticationToken:复制UsernamePasswordAuthenticationToken,把没用的去掉

package com.imooc.security.core.authentication.mobile;import java.util.Collection;import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;/*** 模仿UsernamePasswordAuthenticationToken写的短信登录token* ClassName: SmsCodeAuthenticationToken * @Description: TODO* @author lihaoyang* @date 2018年3月7日*/
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;//没登陆,放手机号,登录成功,放用户信息private final Object principal;/*** 没登录放手机号* <p>Description: </p>* @param mobile*/public SmsCodeAuthenticationToken(String mobile) {super(null);this.principal = mobile;//没登录放手机号setAuthenticated(false);//没登录
    }public SmsCodeAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;super.setAuthenticated(true); // must use super, as we override
    }// ~ Methods// ========================================================================================================public Object getPrincipal() {return this.principal;}public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {if (isAuthenticated) {throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");}super.setAuthenticated(false);}@Overridepublic void eraseCredentials() {super.eraseCredentials();}@Overridepublic Object getCredentials() {return null;}
}

2,SmsCodeAuthenticationFilter,参考UsernamePasswordAuthenticationFilter

package com.imooc.security.core.authentication.mobile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;/*** 模仿UsernamePasswordAuthenticationFilter 写的短信验证码过滤器* ClassName: SmsCodeAuthenticationFilter * @Description: TODO* @author lihaoyang* @date 2018年3月8日*/
public class SmsCodeAuthenticationFilter extends
AbstractAuthenticationProcessingFilter{public static final String IMOOC_FORM_MOBILE_KEY = "mobile";private String mobilePatameter = IMOOC_FORM_MOBILE_KEY;private boolean postOnly = true;// ~ Constructors// ===================================================================================================public SmsCodeAuthenticationFilter() {//过滤的请求url,登录表单的urlsuper(new AntPathRequestMatcher("/authentication/mobile", "POST"));}// ~ Methods// ========================================================================================================public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {if (postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}String mobile = obtainMobile(request);if (mobile == null) {mobile = "";}mobile = mobile.trim();SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);// Allow subclasses to set the "details" property
        setDetails(request, authRequest);//在这里把SmsCodeAuthenticationToken交给AuthenticationManagerreturn this.getAuthenticationManager().authenticate(authRequest);}/*** 获取手机号* @Description: TODO* @param @param request* @param @return   * @return String  * @throws* @author lihaoyang* @date 2018年3月7日*/private String obtainMobile(HttpServletRequest request) {return request.getParameter(mobilePatameter);}
protected void setDetails(HttpServletRequest request,SmsCodeAuthenticationToken authRequest) {authRequest.setDetails(authenticationDetailsSource.buildDetails(request));}
public void setPostOnly(boolean postOnly) {this.postOnly = postOnly;}}

3,SmsCodeAuthenticationProvider:

在 SmsCodeAuthenticationFilter 里 attemptAuthentication方法的最后, return this.getAuthenticationManager().authenticate(authRequest);这句话就是进到 SmsCodeAuthenticationProvider 先调用 supports() 方法,通过后,再调用 authenticate()方法进行认证

package com.imooc.security.core.authentication.mobile;import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;/*** AuthenticationManager 认证时候需要用的一个Provider* ClassName: SmsCodeAuthenticationProvider * @Description: TODO* @author lihaoyang* @date 2018年3月8日*/
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {private UserDetailsService userDetailsService;/*** 认证*/@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {//能进到这说明authentication是SmsCodeAuthenticationToken,转一下SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken)authentication;//token.getPrincipal()就是手机号 mobileUserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());//认证没通过if(user == null){throw new InternalAuthenticationServiceException("无法获取用户信息");}//认证通过SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());//把认证之前得token里存的用户信息赋值给认证后的token对象
        authenticationResult.setDetails(authenticationToken.getDetails());return authenticationResult;}/*** 告诉AuthenticationManager,如果是SmsCodeAuthenticationToken的话用这个类处理*/@Overridepublic boolean supports(Class<?> authentication) {//判断传进来的authentication是不是SmsCodeAuthenticationToken类型的return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);}public UserDetailsService getUserDetailsService() {return userDetailsService;}public void setUserDetailsService(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}}

短信 验证码过滤器,照着图片验证码过滤器写,其实可以重构,不会弄:

package com.imooc.security.core.validate.code;import java.io.IOException;
import java.util.HashSet;
import java.util.Set;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;import com.imooc.security.core.properties.SecurityConstants;
import com.imooc.security.core.properties.SecurityProperties;/*** 短信验证码过滤器* ClassName: ValidateCodeFilter * @Description:*  继承OncePerRequestFilter:spring提供的工具,保证过滤器每次只会被调用一次*  实现 InitializingBean接口的目的:*      在其他参数都组装完毕的时候,初始化需要拦截的urls的值* @author lihaoyang* @date 2018年3月2日*/
public class SmsCodeFilter extends OncePerRequestFilter implements InitializingBean{private Logger logger = LoggerFactory.getLogger(getClass());//认证失败处理器private AuthenticationFailureHandler authenticationFailureHandler;//获取session工具类private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();//需要拦截的url集合private Set<String> urls = new HashSet<>();//读取配置private SecurityProperties securityProperties;//spring工具类private AntPathMatcher antPathMatcher = new AntPathMatcher();/*** 重写InitializingBean的方法,设置需要拦截的urls*/@Overridepublic void afterPropertiesSet() throws ServletException {super.afterPropertiesSet();//读取配置的拦截的urlsString[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getSms().getUrl(), ",");//如果配置了需要验证码拦截的url,不判断,如果没有配置会空指针if(configUrls != null && configUrls.length > 0){for (String configUrl : configUrls) {logger.info("ValidateCodeFilter.afterPropertiesSet()--->配置了验证码拦截接口:"+configUrl);urls.add(configUrl);}}else{logger.info("----->没有配置拦验证码拦截接口<-------");}//短信验证码登录一定拦截
        urls.add(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE);}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {//如果是 登录请求 则执行
//        if(StringUtils.equals("/authentication/form", request.getRequestURI())
//                &&StringUtils.equalsIgnoreCase(request.getMethod(), "post")){
//            try {
//                validate(new ServletWebRequest(request));
//            } catch (ValidateCodeException e) {
//                //调用错误处理器,最终调用自己的
//                authenticationFailureHandler.onAuthenticationFailure(request, response, e);
//                return ;//结束方法,不再调用过滤器链
//            }
//        }/*** 可配置的验证码校验* 判断请求的url和配置的是否有匹配的,匹配上了就过滤*/boolean action = false;for(String url:urls){if(antPathMatcher.match(url, request.getRequestURI())){action = true;}}if(action){try {validate(new ServletWebRequest(request));} catch (ValidateCodeException e) {//调用错误处理器,最终调用自己的
                authenticationFailureHandler.onAuthenticationFailure(request, response, e);return ;//结束方法,不再调用过滤器链
            }}//不是登录请求,调用其它过滤器链
        filterChain.doFilter(request, response);}/*** 校验验证码* @Description: 校验验证码* @param @param request* @param @throws ServletRequestBindingException   * @return void  * @throws ValidateCodeException* @author lihaoyang* @date 2018年3月2日*/private void validate(ServletWebRequest request) throws ServletRequestBindingException {//拿出session中的ImageCode对象ValidateCode smsCodeInSession = (ValidateCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY_SMS);//拿出请求中的验证码String imageCodeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "smsCode");//校验if(StringUtils.isBlank(imageCodeInRequest)){throw new ValidateCodeException("验证码不能为空");}if(smsCodeInSession == null){throw new ValidateCodeException("验证码不存在,请刷新验证码");} if(smsCodeInSession.isExpired()){//从session移除过期的验证码
            sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY_SMS);throw new ValidateCodeException("验证码已过期,请刷新验证码");}if(!StringUtils.equalsIgnoreCase(smsCodeInSession.getCode(), imageCodeInRequest)){throw new ValidateCodeException("验证码错误");}//验证通过,移除session中验证码
        sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY_SMS);}public AuthenticationFailureHandler getAuthenticationFailureHandler() {return authenticationFailureHandler;}public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {this.authenticationFailureHandler = authenticationFailureHandler;}public SecurityProperties getSecurityProperties() {return securityProperties;}public void setSecurityProperties(SecurityProperties securityProperties) {this.securityProperties = securityProperties;}}

把新建的这三个类做下配置,让spring security知道

SmsCodeAuthenticationSecurityConfig:

package com.imooc.security.core.authentication.mobile;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;/*** 短信验证码配置* ClassName: SmsCodeAuthenticationSecurityConfig * @Description: TODO* @author lihaoyang* @date 2018年3月8日*/
@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {@Autowiredprivate AuthenticationFailureHandler imoocAuthenticationFailureHandler;@Autowiredprivate AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;@Autowiredprivate UserDetailsService userDetailsService;@Overridepublic void configure(HttpSecurity http) throws Exception {//1,配置短信验证码过滤器SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));//设置认证失败成功处理器
        smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(imoocAuthenticationSuccessHandler);smsCodeAuthenticationFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);//配置pproviderSmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);http.authenticationProvider(smsCodeAuthenticationProvider).addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);}}

最后在BrowserSecurityConfig里配置短信验证码

@Configuration //这是一个配置
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{//读取用户配置的登录页配置
    @Autowiredprivate SecurityProperties securityProperties;//自定义的登录成功后的处理器
    @Autowiredprivate AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;//自定义的认证失败后的处理器
    @Autowiredprivate AuthenticationFailureHandler imoocAuthenticationFailureHandler;//数据源
    @Autowiredprivate DataSource dataSource;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;@Autowiredprivate SpringSocialConfigurer imoocSocialSecurityConfig;//注意是org.springframework.security.crypto.password.PasswordEncoder
    @Beanpublic PasswordEncoder passwordencoder(){//BCryptPasswordEncoder implements PasswordEncoderreturn new BCryptPasswordEncoder();}/*** 记住我TokenRepository配置,在登录成功后执行* 登录成功后往数据库存token的* @Description: 记住我TokenRepository配置* @param @return   JdbcTokenRepositoryImpl* @return PersistentTokenRepository  * @throws* @author lihaoyang* @date 2018年3月5日*/@Beanpublic PersistentTokenRepository persistentTokenRepository(){JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();jdbcTokenRepository.setDataSource(dataSource);//启动时自动生成相应表,可以在JdbcTokenRepositoryImpl里自己执行CREATE_TABLE_SQL脚本生成表//第二次启动表已存在,需要注释
//        jdbcTokenRepository.setCreateTableOnStartup(true);return jdbcTokenRepository;}//版本二:可配置的登录页
    @Overrideprotected void configure(HttpSecurity http) throws Exception {//~~~-------------> 图片验证码过滤器 <------------------ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();//验证码过滤器中使用自己的错误处理
        validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);//配置的验证码过滤url
        validateCodeFilter.setSecurityProperties(securityProperties);validateCodeFilter.afterPropertiesSet();//~~~-------------> 短信验证码过滤器 <------------------SmsCodeFilter smsCodeFilter = new SmsCodeFilter();//验证码过滤器中使用自己的错误处理
        smsCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);//配置的验证码过滤url
        smsCodeFilter.setSecurityProperties(securityProperties);smsCodeFilter.afterPropertiesSet();//实现需要认证的接口跳转表单登录,安全=认证+授权//http.httpBasic() //这个就是默认的弹框认证//
        http  .addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
//            .apply(imoocSocialSecurityConfig)//社交登录
//            .and()//把验证码过滤器加载登录过滤器前边.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)//表单认证相关配置
            .formLogin() .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) //处理用户认证BrowserSecurityController//登录过滤器UsernamePasswordAuthenticationFilter默认登录的url是"/login",在这能改
                .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM) .successHandler(imoocAuthenticationSuccessHandler)//自定义的认证后处理器.failureHandler(imoocAuthenticationFailureHandler) //登录失败后的处理
                .and()//记住我相关配置
            .rememberMe().tokenRepository(persistentTokenRepository())//TokenRepository,登录成功后往数据库存token的.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())//记住我秒数.userDetailsService(userDetailsService) //记住我成功后,调用userDetailsService查询用户信息
            .and()//授权相关的配置
            .authorizeRequests() // /authentication/require:处理登录,securityProperties.getBrowser().getLoginPage():用户配置的登录页
                .antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,securityProperties.getBrowser().getLoginPage(),//放过登录页不过滤,否则报错SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*").permitAll() //验证码.anyRequest()        //任何请求.authenticated()    //都需要身份认证
            .and().csrf().disable() //关闭csrf防护.apply(smsCodeAuthenticationSecurityConfig);//把短信验证码配置应用上
    }
}

访问登陆页,点击发送验证码模拟发送验证码

输入后台打印的验证码

登录成功:

完整代码在github:https://github.com/lhy1234/spring-security

转载于:https://www.cnblogs.com/lihaoyang/p/8523279.html

Spring Security构建Rest服务-0702-短信验证码登录相关推荐

  1. Spring Security OAuth2 优雅的集成短信验证码登录以及第三方登录

    基于SpringCloud做微服务架构分布式系统时,OAuth2.0作为认证的业内标准,Spring Security OAuth2也提供了全套的解决方案来支持在Spring Cloud/Spring ...

  2. 5.Spring Security 短信验证码登录

    Spring Security 短信验证码登录 在 Spring Security 添加图形验证码一节中,我们已经实现了基于 Spring Boot + Spring Security 的账号密码登录 ...

  3. Spring Security 短信验证码登录(5)

    在Spring Security添加图形验证码中,我们已经实现了基于Spring Boot + Spring Security的账号密码登录,并集成了图形验证码功能.时下另一种非常常见的网站登录方式为 ...

  4. Spring Security简单增加短信验证码登录

    查网上资料增加短信验证码登录都要增加一大推,要重头写Spring Security的实现,我呢,只想在原来的密码登录基础上简单实现一下短信验证码登录. 1.首先得先一个认证类,来认证验证码是否正确,这 ...

  5. security模仿密码登录实现短信验证码登录

    security模仿密码登录实现短信验证码登录 模仿UsernamePasswordAuthenticationToken创建短信验证码的token类SmsAuthenticationToken /* ...

  6. OAuth2.0 - 自定义模式授权 - 短信验证码登录

    一.OAuth2.0 - 自定义模式授权 上篇文章我们分析了目前的情况,演示了微服务的大环境下在保证安全的情况下通过SpringGateWay实现统一的鉴权处理,但是前面的演示中,我们都是基于用户名密 ...

  7. SpringBoot OAuth2.0 使用短信验证码登录授权

    SpringBoot OAuth2.0 使用短信验证码登录授权 实现步骤: 自定义授权器,继承 AbstractTokenGranter 类: 重写 getOAuth2Authentication 函 ...

  8. SpringSecurity短信验证码登录

    短信验证码登录 时下另一种非常常见的网站登录方式为手机短信验证码登录,但Spring Security默认只提供了账号密码的登录认证逻辑,所以要实现手机短信验证码登录认证功能,我们需要模仿Spring ...

  9. 手把手带你在集成SpringSecurity的SpringBoot应用中添加短信验证码登录认证功能

    本文目录 前言 1 自定义AuthenticationToken类 2 自定义AuthenticationProvider类 3 自定义MobilePhoneAuthenticationFilter ...

最新文章

  1. mysql+在服务中无法启动_MySQL服务初始化后无法启动
  2. Quartz.NET实际
  3. 实现跨域cookie共享(转载)
  4. 初识vue之axios的封装
  5. python爬虫04--有道翻译
  6. python怎么创建字符串_Python 字符串
  7. 《You can do it!》的chapter 2笔记
  8. 搭建 Harbor 1.10.6 高可用集群
  9. 一个简单的Web服务器
  10. k3 审核流程图_3 金蝶K3操作流程图详解
  11. 【系统分析师之路】2010年系统分析师上午综合知识真题
  12. android 直播 h5,H5移动端直播的要点
  13. 飞火流星测试经验总结
  14. 笔记本电脑怎么做服务器的显示屏,干货!笔记本液晶屏不要扔,自己动手DIY便携显示器...
  15. 最高法规定网络转载涉嫌侵权需担责 10月10日起施行
  16. (UPCOJ暑期训练)Tally Counters
  17. 四种静态路由配置方法
  18. Python BeautifulSoup4 入门使用
  19. python 大数据量绘图_Matplotlib绘图遇到时间刻度就犯难?现在,一次性告诉你四种方法...
  20. 如何实现复杂的问题简单化

热门文章

  1. DBA(六):MHA集群
  2. 公有云 --- 华为云的基本运用
  3. .net转换关键字:operator、explicit与implicit
  4. ant编译java工程总结
  5. 面试官问:平常你是怎么对 Java 服务进行调优的?
  6. 这 17 个 JVM 参数,高级 Java 必须掌握!
  7. 结合代码详细聊聊 Java 网络编程中的 BIO、NIO 和 AIO
  8. 互联网架构“高并发”到底怎么玩?
  9. 架构师口中的混沌工程,究竟用来解决什么问题
  10. 阿里巴巴高级技术专家:不要用沉默的方式一味地迎合别人的要求,据理力争或许才是作为的表现