• Maven搭建Spring Security3.2项目详解


    本来是打算在上一篇SpringMVC+Hibernate上写的,结果发现上面那篇 一起整合的,结果发现上一篇内容实在是太长了,就另起一篇,这篇主要是采用 Maven搭建Spring+SpringMVC+Hibernate+Security整合,而Spring+SpringMVC+Hibernate 已经在上一篇介绍了,在这篇将不再重复写了,主要说明一下SpringSecurity3.2权限控制整合搭建,以及配置,使用注意事项等。

            SpringSecurity的Api文档地址:查看

    1、Maven映入SpringSecurity依赖包

          在pom.xml中引入我们需要引入spring-security-core,spring-security-config,spring-security-taglibs三个包,如下

        

    1.              <!-- spring-security -->  
    2. <dependency>  
    3.     <groupId>org.springframework.security</groupId>  
    4.     <artifactId>spring-security-core</artifactId>  
    5.     <version>${security.version}</version>  
    6. </dependency>  
    7.   
    8. <dependency>  
    9.     <groupId>org.springframework.security</groupId>  
    10.     <artifactId>spring-security-config</artifactId>  
    11.     <version>${security.version}</version>  
    12. </dependency>  
    13.   
    14. <dependency>  
    15.     <groupId>org.springframework.security</groupId>  
    16.     <artifactId>spring-security-taglibs</artifactId>  
    17.     <version>${security.version}</version>  
    18. </dependency>  


    2、配置security的配置文件

          我们新建一个配置文件(起名随意),我这儿就叫spring-security.xml,我现在定义了几个权限,权限信息表内容如下:

        

      其中:管理用户和全部用户的权限将通过jsp中security标签配置,其他的通过spring-security.xml文件配置。

      先贴出spring-security.xml的文件,详细配置含义我将做一个简单的说明:

        

    1. <span style="font-size:14px;"><?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:security="http://www.springframework.org/schema/security"  
    4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    5.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
    6.                         http://www.springframework.org/schema/beans/spring-beans-4.1.xsd  
    7.                         http://www.springframework.org/schema/security   
    8.                         http://www.springframework.org/schema/security/spring-security-3.2.xsd"  
    9.     default-lazy-init="true">  
    10.   
    11.     <description>spring-security配置</description>  
    12.   
    13.     <!-- 静态资源 -->  
    14.     <security:http pattern="/css/**" security="none" />  
    15.     <security:http pattern="/js/**" security="none" />  
    16.     <security:http pattern="/images/**" security="none" />  
    17.   
    18.     <security:http>  
    19.         <security:intercept-url pattern="/user/save*"  
    20.             access="ROLE_添加用户" requires-channel="any" />  
    21.         <security:intercept-url pattern="/user/delete*"  
    22.             access="ROLE_删除用户" requires-channel="any" />  
    23.         <security:intercept-url pattern="/user/user*"  
    24.             access="ROLE_浏览用户" requires-channel="any" />  
    25.         <security:intercept-url pattern="/user/update*"  
    26.             access="ROLE_修改用户" requires-channel="any" />  
    27.   
    28.         <security:session-management>  
    29.             <security:concurrency-control  
    30.                 expired-url="/login/login.htmls?repeat=true" max-sessions="1"  
    31.                 error-if-maximum-exceeded="true" />  
    32.         </security:session-management>  
    33.   
    34.         <security:form-login login-page="/login/login.htmls"  
    35.             authentication-failure-url="/login/login.htmls?error=true"  
    36.             default-target-url="/user/main.htmls" always-use-default-target='true'  
    37.             username-parameter="nickName" password-parameter="nickPassword" />  
    38.   
    39.         <security:logout invalidate-session="true"  
    40.             logout-success-url="/login/login.htmls?logout=true" />  
    41.   
    42.     </security:http>  
    43.   
    44.     <!-- 认证配置 自定义认证实现UserDetailsService接口 -->  
    45.     <security:authentication-manager>  
    46.         <security:authentication-provider  
    47.             user-service-ref="userDetailsService">  
    48.             <!-- 配置密码加密方式 -->  
    49.             <security:password-encoder hash="md5" />  
    50.         </security:authentication-provider>  
    51.     </security:authentication-manager>  
    52.   
    53.     <bean id="userDetailsService" class="org.andy.work.service.impl.UserDetailsServiceImpl" />  
    54.       
    55. </beans></span>  


      其中:我们配置了静态文件管理,session对话管理,登录管理,注销配置,权限配置,自定义数据表权限认证配置。

    2.1、静态文件管理

        我们对于css,image,js这些不用权限拦截。

    2.2、session对话管理

       session管理max-sessions="1"配置了最多有一个用户登录,(在wab.xml还要添加如下:)

             

    1. <span style="font-size:14px;"><!-- spring-security 管理session配置 -->  
    2.     <listener>  
    3.         <listener-class>  
    4.             org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>  
    5.     </listener></span>  


        error-if-maximum-exceeded="true"  值为true时,若果有一个用户登录,第二个用户无法登陆,

                                                                      值为false时,若果有一个用户已经登录,下一个用户登录将踢掉上一个用户。

        自然在session-manager中我们可以配置session失效时,跳转的url如,invalid-session- url="/invalidSession.jsp",如果session失效时,刷新将跳转到invalidSession.jsp页面。

    2.3、登录管理

        通过security:form-login配置登录,login-page为登录跳转的url,authentication-failure-url为登录失败时的url(次url可以不存在),

       username-parameter和password-parameter为登录表单是用户名和密码,如果不写默认为j_password和j_username

       login-processing-url:为登录时表单action跳转的url,如果不填写默认为 j_spring_security_check。

    2.4、注销管理

       通过security:logout标签配置注销,invalidate-session:注销时session是否失效。logout-success-url:注销成功后跳转的地址。

    logout-url:为注销的url,如果不填写,则默认为j_spring_security_logout

    2.5、权限配置

       security:intercept-url为要拦截权限认证的的url,pattern为拦截的正则匹配url,access:所需的权限,可以是一 个权限组,用逗号隔开,requires-channel:拦截http还是https的,如果两个都拦截用any。

    2.6、配置自定义的权限认证机制

        通过我们数据库的权限表信息,我们自定义权限认证机制,需要我们实现UserDetailsService接口,配置用户密码的加密方式。

    security:password-encoder:配置密码加密规则。

    3、web.xml容器配置

       

    1. <span style="font-size:14px;">         <context-param>  
    2.         <param-name>contextConfigLocation</param-name>  
    3.         <param-value>  
    4.             classpath:spring.xml  
    5.             classpath:spring-hibernate.xml  
    6.             classpath:spring-security.xml  
    7.         </param-value>  
    8.     </context-param>  
    9.   
    10.        <!-- Spring-Security filter 最好配置在控制层filter的前面 -->  
    11.     <filter>  
    12.         <filter-name>springSecurityFilterChain</filter-name>  
    13.         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
    14.     </filter>  
    15.     <filter-mapping>  
    16.         <filter-name>springSecurityFilterChain</filter-name>  
    17.         <url-pattern>/*</url-pattern>  
    18.     </filter-mapping>  
    19.   
    20.     <!-- spring-security 管理session配置 -->  
    21.     <listener>  
    22.         <listener-class>  
    23.             org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>  
    24.     </listener></span>  

     

    配置了加载的spring-security文件,security拦截的filter,以及security的session监听。

    4、自定义认证,实现UserDetailsService接口

    UserDetailsServiceImpl如下:

    1. <span style="font-size:14px;">package org.andy.work.service.impl;  
    2.   
    3. import java.util.HashSet;  
    4. import java.util.Set;  
    5.   
    6. import org.andy.work.dao.UserDao;  
    7. import org.andy.work.entity.AcctAuthority;  
    8. import org.andy.work.entity.AcctRole;  
    9. import org.andy.work.entity.AcctUser;  
    10. import org.apache.log4j.Logger;  
    11. import org.springframework.beans.factory.annotation.Autowired;  
    12. import org.springframework.security.core.GrantedAuthority;  
    13. import org.springframework.security.core.authority.SimpleGrantedAuthority;  
    14. import org.springframework.security.core.userdetails.User;  
    15. import org.springframework.security.core.userdetails.UserDetails;  
    16. import org.springframework.security.core.userdetails.UserDetailsService;  
    17. import org.springframework.security.core.userdetails.UsernameNotFoundException;  
    18.   
    19. /**  
    20.  * 创建时间:2015-2-9 下午5:24:44  
    21.  *   
    22.  * @author andy  
    23.  * @version 2.2  
    24.  *          <p>  
    25.  *          描述: 实现SpringSecurity的UserDetails接口 自定义认证  
    26.  */  
    27.   
    28. public class UserDetailsServiceImpl implements UserDetailsService {  
    29.   
    30.     private static final Logger LOGGER = Logger  
    31.             .getLogger(UserDetailsServiceImpl.class);  
    32.   
    33.     // 注入查询User的dao层  
    34.     @Autowired  
    35.     private UserDao userDao;  
    36.   
    37.     @Override  
    38.     public UserDetails loadUserByUsername(String username)  
    39.             throws UsernameNotFoundException {  
    40.         LOGGER.info("认证用户:" + username);  
    41.   
    42.         // 查询数据库获取改用户的信息  
    43.         AcctUser acctUser = userDao.findByNickName(username);  
    44.   
    45.         if (null == acctUser) {  
    46.             throw new UsernameNotFoundException("用户:" + username + "不存在");  
    47.         }  
    48.   
    49.         Set<GrantedAuthorityauthorities = getAuthorities(acctUser);  
    50.   
    51.         // 将没有使用到的属性设置为true  
    52.         UserDetails userDetails = new User(acctUser.getNickName(),  
    53.                 acctUser.getNickPassword(), true, true, true, true, authorities);  
    54.   
    55.         return userDetails;  
    56.     }  
    57.   
    58.     // 获得用户所有角色的权限  
    59.     private Set<GrantedAuthority> getAuthorities(AcctUser acctUser) {  
    60.         Set<GrantedAuthorityauthoritySet = new HashSet<GrantedAuthority>();  
    61.   
    62.         // 默认所有的用户有"浏览用户"的权利  
    63.         authoritySet.add(new SimpleGrantedAuthority("ROLE_浏览用户"));  
    64.   
    65.         // 依次添加  
    66.         if (null != acctUser.getAcctRoles()  
    67.                 && acctUser.getAcctRoles().size() > 0)  
    68.             for (AcctRole role : acctUser.getAcctRoles()) {  
    69.                 if (null != role.getAcctAuthorities()  
    70.                         && role.getAcctAuthorities().size() > 0)  
    71.                     for (AcctAuthority authority : role.getAcctAuthorities()) {  
    72.                         authoritySet.add(new SimpleGrantedAuthority(authority  
    73.                                 .getPrefixedName()));  
    74.                     }  
    75.             }  
    76.   
    77.         return authoritySet;  
    78.     }  
    79.   
    80. }  
    81. </span>  


    涉及到的AcctUser,AcctRole,AcctAuthority类我就不贴了,不然一大把,我会提供源码。

    5、Security的认证错误提示

        我们可以通过sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message获取认证错误。

    6、Security获取session中的用户名和用户信息

         security将用户信息存放在session中,可以通过以下两种获得:

    6.1、后台获取

        

    1. <span style="font-size:14px;">        //获取security的上下文  
    2.         SecurityContext securityContext = SecurityContextHolder.getContext();  
    3.         //获取认证对象  
    4.         Authentication authentication = securityContext.getAuthentication();  
    5.         //在认证对象中获取主体对象  
    6.         Object principal = authentication.getPrincipal();  
    7.           
    8.         String username = "";  
    9.         if(principal instanceof UserDetails){  
    10.             username = ((UserDetails) principal).getUsername();  
    11.         }else {  
    12.             username = principal.toString();  
    13.         }</span>  


    6.1、前台获取

        先引入security的标签库:

    1. <span style="font-size:14px;"><%@taglib prefix="security" uri="http://www.springframework.org/security/tags"%></span>  


    在查看:

    1. <span style="font-size:14px;"><security:authentication property="name"/></span>  


    7、前台标签授权

        通过security:authorize标签设置权限,其有三种属性分别如下:

            ifAnyGranted::只有当前用户拥有所指定的权限中的一个的时候,就能显示标签内部的内容(相当于“或”的关系)

              ifAllGranted:只有当前用户拥有所指定的权限时,才显示标签的内容(相当于“与”的关系)

              ifNotGranted:  没有指定的权限的时候,显示标签内容(相当于“非”的关系)

    自然也可以通过method限制是那种http的请求(http的请求有8种:- GET - DELETE - HEAD - OPTIONS    - POST     - PUT    - PATCH    - TRACE)。

    8、后台Controller

    LoginController

    1. <span style="font-size:14px;"><span style="font-size:14px;">package org.andy.work.controller;  
    2.   
    3. import org.andy.work.entity.AcctUser;  
    4. import org.apache.log4j.Logger;  
    5. import org.springframework.stereotype.Controller;  
    6. import org.springframework.validation.Errors;  
    7. import org.springframework.web.bind.annotation.ModelAttribute;  
    8. import org.springframework.web.bind.annotation.RequestMapping;  
    9. import org.springframework.web.bind.annotation.RequestParam;  
    10.   
    11. /** 
    12.  * 创建时间:2015-2-10 下午9:23:34 
    13.  *  
    14.  * @author andy 
    15.  * @version 2.2 描述: 
    16.  */  
    17. @Controller  
    18. @RequestMapping("/login")  
    19. public class LoginController {  
    20.   
    21.     private static final Logger LOGGER = Logger  
    22.             .getLogger(LoginController.class);  
    23.   
    24.     @RequestMapping("/login")  
    25.     public String login(@ModelAttribute AcctUser acctUser,  
    26.             @RequestParam(required = false) Boolean logout,  
    27.             Errors errors  
    28.             ) {  
    29.         LOGGER.info("login");  
    30.           
    31.         if(null != logout){  
    32.             errors.reject("msg", "已经安全退出");  
    33.         }  
    34.           
    35.         return "/login/login";  
    36.     }  
    37.   
    38.   
    39. }</span>  
    40. </span>  


    UserController类 

    1. <span style="font-size:14px;">package org.andy.work.controller;  
    2.   
    3. import java.util.List;  
    4.   
    5. import org.andy.work.entity.AcctUser;  
    6. import org.andy.work.service.UserService;  
    7. import org.apache.log4j.Logger;  
    8. import org.springframework.beans.factory.annotation.Autowired;  
    9. import org.springframework.security.core.Authentication;  
    10. import org.springframework.security.core.context.SecurityContext;  
    11. import org.springframework.security.core.context.SecurityContextHolder;  
    12. import org.springframework.security.core.userdetails.UserDetails;  
    13. import org.springframework.stereotype.Controller;  
    14. import org.springframework.ui.ModelMap;  
    15. import org.springframework.web.bind.annotation.PathVariable;  
    16. import org.springframework.web.bind.annotation.RequestMapping;  
    17. import org.springframework.web.bind.annotation.ResponseBody;  
    18.   
    19. /**   
    20.  * 创建时间:2015-2-7 上午11:49:00   
    21.  * @author andy   
    22.  * @version 2.2   
    23.  * 描述: 用户Controller 
    24.  */  
    25. @Controller  
    26. @RequestMapping("/user")  
    27. public class UserController {  
    28.   
    29.     private static final Logger LOGGER = Logger.getLogger(UserController.class);  
    30.       
    31.     @Autowired  
    32.     private UserService userService;  
    33.       
    34.     @RequestMapping("/showInfo/{userId}")  
    35.     public String showUserInfo(ModelMap modelMap, @PathVariable String userId){  
    36.         LOGGER.info("查询用户:" + userId);  
    37.         AcctUser userInfo = userService.load(userId);  
    38.         modelMap.addAttribute("userInfo", userInfo);  
    39.         return "/user/showInfo";  
    40.     }  
    41.       
    42.     @RequestMapping("/showInfos")  
    43.     public @ResponseBody List<AcctUser> showUserInfos(){  
    44.         LOGGER.info("查询用户全部用户");  
    45.         List<AcctUser> userInfos = userService.findAll();  
    46.         return userInfos;  
    47.     }  
    48.       
    49.       
    50.     @RequestMapping("/main")  
    51.     public String main(ModelMap modelMap){  
    52.         LOGGER.info("显示主页面");  
    53.         //后台获取security保存的session中的用户信息  
    54.           
    55.         //获取security的上下文  
    56.         SecurityContext securityContext = SecurityContextHolder.getContext();  
    57.         //获取认证对象  
    58.         Authentication authentication = securityContext.getAuthentication();  
    59.         //在认证对象中获取主体对象  
    60.         Object principal = authentication.getPrincipal();  
    61.           
    62.         String username = "";  
    63.         if(principal instanceof UserDetails){  
    64.             username = ((UserDetails) principal).getUsername();  
    65.         }else {  
    66.             username = principal.toString();  
    67.         }  
    68.         modelMap.addAttribute("username", username);  
    69.         return "/user/main";  
    70.     }  
    71.       
    72.       
    73.     @RequestMapping("/manage")  
    74.     public String manage(ModelMap modelMap){  
    75.         LOGGER.info("显示主页面");  
    76.         modelMap.addAttribute("msg", "manage");  
    77.         return "/user/option";  
    78.     }  
    79.       
    80.     @RequestMapping("/save")  
    81.     public String save(ModelMap modelMap){  
    82.         LOGGER.info("保存");  
    83.         modelMap.addAttribute("msg", "save");  
    84.         return "/user/option";  
    85.     }  
    86.       
    87.     @RequestMapping("/update")  
    88.     public String update(ModelMap modelMap){  
    89.         LOGGER.info("修改");  
    90.         modelMap.addAttribute("msg", "update");  
    91.         return "/user/option";  
    92.     }  
    93.       
    94.     @RequestMapping("/delete")  
    95.     public String delete(ModelMap modelMap){  
    96.         LOGGER.info("删除");  
    97.         modelMap.addAttribute("msg", "delete");  
    98.         return "/user/option";  
    99.     }  
    100.       
    101. }  
    102. </span>  

     9、前台

    login.jsp


    1. <span style="font-size:14px;"><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
    2. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>  
    3. <%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>  
    4. <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>  
    5. <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>  
    6.   
    7. <%  
    8.     String path = request.getContextPath();  
    9.     String basePath = request.getScheme() + "://"  
    10.             + request.getServerName() + ":" + request.getServerPort()  
    11.             + path + "/";  
    12. %>  
    13. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
    14. <html>  
    15. <head>  
    16. <base href="<%=basePath%>" />  
    17. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
    18. <title>login</title>  
    19. </head>  
    20. <body>  
    21.     <div>${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}</div>  
    22.   
    23.     <form:form action="j_spring_security_check" modelAttribute="acctUser" method="post">  
    24.         <form:errors path="*" cssStyle="color:red;" />  
    25.         <br />  
    26.         用户:<form:input path="nickName" />  
    27.         <br />  
    28.         密码:<form:password path="nickPassword" />  
    29.         <br />  
    30.         <form:button>登录</form:button>  
    31.     </form:form>  
    32. </body>  
    33. </html></span>  


    main.jsp

    1. <span style="font-size:14px;"><%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
    2. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>  
    3. <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>  
    4. <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>  
    5. <%@taglib prefix="security" uri="http://www.springframework.org/security/tags"%>  
    6.   
    7. <%  
    8.     String path = request.getContextPath();  
    9.     String basePath = request.getScheme() + "://"  
    10.             + request.getServerName() + ":" + request.getServerPort()  
    11.             + path + "/";  
    12. %>  
    13. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
    14. <html>  
    15. <head>  
    16. <base href="<%=basePath%>" />  
    17. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
    18. <title>login</title>  
    19. </head>  
    20. <body>  
    21.   
    22.     welcome! <security:authentication property="name"/>  
    23.     <br/>  
    24.           后台获取用户名:${username }  
    25.     <div style="border: 1px; "><target="_blank" href="j_spring_security_logout">注销</a></div>  
    26.       
    27.     <br/>  
    28.       
    29.     <security:authorize ifAnyGranted="ROLE_浏览用户">  
    30.     <div>  
    31.         <target="_blank" href="user/showInfo.htmls">全部用户</a>  
    32.     </div>  
    33.     </security:authorize>  
    34.       
    35.       
    36.     <security:authorize ifAllGranted="ROLE_管理用户">  
    37.         <div>  
    38.             <target="_blank" href="user/manage.htmls">管理用户</a>  
    39.         </div>  
    40.     </security:authorize>  
    41.       
    42.     <div>  
    43.         <target="_blank" href="user/save.htmls">添加用户</a>  
    44.     </div>  
    45.     <div>  
    46.         <target="_blank" href="user/update.htmls">修改部用户</a>  
    47.     </div>  
    48.     <div>  
    49.         <target="_blank" href="user/delete.htmls">删除用户</a>  
    50.     </div>  
    51.       
    52. </body>  
    53. </html></span>  


    10、测试

        现在andy用户拥有“浏览用户”和“添加用户”权限

    用户密码错误时:

        

      正确密码登陆后:

         

        

    点击添加用户,正确跳转。如下:




    点击全部用户,无权限提示。

    11、Spring security自定义认证错误提示

        首先,拷贝spring-security-core写的messages_zh_CN.properties国际化文件,放到项目的src/main/resources目录中

              修改对应的提示,按照我们自己的需求,我命名为messages.properties

       其次, 我们需在spring的配置文件中添加如下内容:

    1. <span style="font-size:14px;"><span style="font-size:14px;">    <!-- 定义上下文返回的消息的国际化 -->  
    2.     <bean id="messageSource"  
    3.         class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
    4.         <property name="basename" value="classpath:messages" />  
    5.     </bean>  
    6.   
    7.     <bean id="localeResolver"  
    8.         class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" /></span></span>  


    ok,Maven搭建Spring+Hibernate+security整合完毕。


    后续

    遗留问题,通用的重写security提示信息,其他的都能国际化汉语提示,但是

    唯独

    ConcurrentSessionControlStrategy.exceededAllowed=u5DF2u7ECFu6709 {0} u4E2Au7528u6237u767Bu5F55uFF0Cu4E0Du80FDu91CDu590Du767Bu5F55

    我修改重复登录提示时,还是security原来自带的提示,如下:

         


    希望又遇到的共同留言探讨。


    博客地址http://blog.csdn.net/fengshizty

    源码地址:http://download.csdn.net/detail/fengshizty/8444061


  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    C 语言编程 — 使用 assert 断言进行程序设计
    C 语言编程 — uint8_t / uint16_t / uint32_t /uint64_t
    五月数据库技术通讯丨Oracle 12c因新特性引发异常Library Cache Lock等待
    4场直播丨站撸Oracle、MySQL、医疗、航空
    asyncio
    python 多线程 多进程
    一文详解被阿里腾讯视作核心机密的大数据平台架构
  • 原文地址:https://www.cnblogs.com/lykxqhh/p/5691236.html
Copyright © 2020-2023  润新知