Spring Security 有两个作用:认证和授权

一、Srping security 4 filter 别名及顺序

spring security 4 标准filter别名和顺序,因为经常要用就保存到自己博客吧  点击访问官网链接

Table 6.1. Standard Filter Aliases and Ordering

Alias Filter Class Namespace Element or Attribute

CHANNEL_FILTER

ChannelProcessingFilter

http/intercept-url@requires-channel

SECURITY_CONTEXT_FILTER

SecurityContextPersistenceFilter

http

CONCURRENT_SESSION_FILTER

ConcurrentSessionFilter

session-management/concurrency-control

HEADERS_FILTER

HeaderWriterFilter

http/headers

CSRF_FILTER

CsrfFilter

http/csrf

LOGOUT_FILTER

LogoutFilter

http/logout

X509_FILTER

X509AuthenticationFilter

http/x509

PRE_AUTH_FILTER

AbstractPreAuthenticatedProcessingFilterSubclasses

N/A

CAS_FILTER

CasAuthenticationFilter

N/A

FORM_LOGIN_FILTER

UsernamePasswordAuthenticationFilter

http/form-login

BASIC_AUTH_FILTER

BasicAuthenticationFilter

http/http-basic

SERVLET_API_SUPPORT_FILTER

SecurityContextHolderAwareRequestFilter

http/@servlet-api-provision

JAAS_API_SUPPORT_FILTER

JaasApiIntegrationFilter

http/@jaas-api-provision

REMEMBER_ME_FILTER

RememberMeAuthenticationFilter

http/remember-me

ANONYMOUS_FILTER

AnonymousAuthenticationFilter

http/anonymous

SESSION_MANAGEMENT_FILTER

SessionManagementFilter

session-management

EXCEPTION_TRANSLATION_FILTER

ExceptionTranslationFilter

http

FILTER_SECURITY_INTERCEPTOR

FilterSecurityInterceptor

http

SWITCH_USER_FILTER

SwitchUserFilter

N/A

二、Spring security filter作用

2.1 默认filter链

在程序启动时会打印出如下日志,该日志打印出了默认的filter链和顺序,其中SecurityContextPersistenceFilter为第一个filter,FilterSecurityInterceptor为最后一个filter。

2018-02-11 15:24:17,204  INFO DefaultSecurityFilterChain - Creating filter chain:
org.springframework.security.web.util.matcher.AnyRequestMatcher@1,
[org.springframework.security.web.context.SecurityContextPersistenceFilter@3cf3957d,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7ff34bd,
org.springframework.security.web.header.HeaderWriterFilter@4dad11a2,
org.springframework.security.web.authentication.logout.LogoutFilter@5be6ee89,
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@5426eed3,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5da2a66c,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@23169e35,
org.springframework.security.web.session.SessionManagementFilter@5b1627ea,
org.springframework.security.web.access.ExceptionTranslationFilter@70b913f5,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@2dfe7327]

2.2 默认filter链作用

默认有10条过滤链,下面逐个看下去。

2.2.1 /index.html at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'

SecurityContextPersistenceFilter 两个主要职责:

a.请求到来时,通过HttpSessionSecurityContextRepository接口从Session中读取SecurityContext,如果读取结果为null,则创建之。

 public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
HttpServletRequest request = requestResponseHolder.getRequest();
HttpServletResponse response = requestResponseHolder.getResponse();
HttpSession httpSession = request.getSession(false);
// 从session中获取SecurityContext
SecurityContext context = readSecurityContextFromSession(httpSession); if (context == null) {
if (logger.isDebugEnabled()) {
logger.debug("No SecurityContext was available from the HttpSession: "
+ httpSession + ". " + "A new one will be created.");
}
// 未读取到SecurityContext则新建一个SecurityContext
context = generateNewContext(); } SaveToSessionResponseWrapper wrappedResponse = new SaveToSessionResponseWrapper(
response, request, httpSession != null, context);
requestResponseHolder.setResponse(wrappedResponse); if (isServlet3) {
requestResponseHolder.setRequest(new Servlet3SaveToSessionRequestWrapper(
request, wrappedResponse));
} return context;
}

获得SecurityContext之后,会将其存入SecurityContextHolder,其中SecurityContextHolder默认是ThreadLocalSecurityContextHolderStrategy实例

 private static void initialize() {
if ((strategyName == null) || "".equals(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
} if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
}
// 以下内容省略
}

ThreadLocalSecurityContextHolderStrategy中的ContextHolder定义如下,注意这是一个ThreadLocal变量,线程局部变量。

 private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>();

b.请求结束时清空SecurityContextHolder,并将SecurityContext保存到Session中。

 finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything
// else.
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
request.removeAttribute(FILTER_APPLIED); if (debug) {
logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}

2.2.2 /index.html at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'

提供了对securityContext和WebAsyncManager的集成,其会把SecurityContext设置到异步线程中,使其也能获取到用户上下文认证信息。

 @Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager
.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
if (securityProcessingInterceptor == null) {
asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY,
new SecurityContextCallableProcessingInterceptor());
} filterChain.doFilter(request, response);
}

2.2.3 /index.html at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'

用来给http response添加一些Header,比如X-Frame-Options、X-XSS-Protection*、X-Content-Type-Options。

 protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { HeaderWriterResponse headerWriterResponse = new HeaderWriterResponse(request,
response, this.headerWriters);
try {
filterChain.doFilter(request, headerWriterResponse);
}
finally {
// 向response header中添加header
headerWriterResponse.writeHeaders();
}
}

2.2.4 /index.html at position 4 of 10 in additional filter chain; firing Filter: 'LogoutFilter'

处理退出登录的Filter,如果请求的url为/logout则会执行退出登录操作。

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 判断是否需要logout,判断request url是否匹配/logout
if (requiresLogout(request, response)) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (logger.isDebugEnabled()) {
logger.debug("Logging out user '" + auth
+ "' and transferring to logout destination");
}
// 执行一系列的退出登录操作
for (LogoutHandler handler : handlers) {
handler.logout(request, response, auth);
}
// 退出成功,执行logoutSuccessHandler进行重定向等操作
logoutSuccessHandler.onLogoutSuccess(request, response, auth); return;
} chain.doFilter(request, response);
}

2.2.5 /index.html at position 5 of 10 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'

表单认证是最常用的一个认证方式,一个最直观的业务场景便是允许用户在表单中输入用户名和密码进行登录,而这背后的UsernamePasswordAuthenticationFilter,在整个Spring Security的认证体系中则扮演着至关重要的角色。

UsernamePasswordAuthenticationFilter是继承自AbstractAuthenticationProcessingFilter。

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 判断是否需要执行登录认证,判断request url 是否能匹配/login
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response); return;
} if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
} Authentication authResult; try {
// UsernamePasswordAuthenticationFilter 实现该方法
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// 子类未完成认证,立即返回
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
// 在认证过程中抛出异常
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
unsuccessfulAuthentication(request, response, failed); return;
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed); return;
} // Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
} successfulAuthentication(request, response, chain, authResult);
}

在UsernamePasswordAuthenticationFilter中实现了类attemptAuthentication,不过该类只实现了一个非常简化的版本,如果真的需要通过表单登录,是需要自己继承UsernamePasswordAuthenticationFilter并重载attemptAuthentication方法的。

在AbstractAuthenticationProcessingFilter的doFilter方法中一开始是判断是否有必要进入到认证filter,这个过程其实是判断request url是否匹配/login,当然也可以通过filterProcessesUrl属性去配置匹配所使用的pattern。

2.2.6 /index.html at position 6 of 10 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'

将request存到session中,用于缓存request请求,可以用于恢复被登录而打断的请求

 public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 从session中获取与当前request匹配的缓存request,并将缓存request从session删除
HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest(
(HttpServletRequest) request, (HttpServletResponse) response);
// 如果requestCache中缓存了request,则使用缓存的request
chain.doFilter(wrappedSavedRequest == null ? request : wrappedSavedRequest,
response);
}

此处从session中取出request,存储request是在ExceptionTranslationFilter中。具体可以参考探究 Spring Security 缓存请求

2.2.7 /index.html at position 7 of 10 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'

此过滤器对ServletRequest进行了一次包装,使得request具有更加丰富的API

     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(this.requestFactory.create((HttpServletRequest) req,
(HttpServletResponse) res), res);
}

2.2.8 /index.html at position 8 of 10 in additional filter chain; firing Filter: 'SessionManagementFilter'

和session相关的过滤器,内部维护了一个SessionAuthenticationStrategy,两者组合使用,常用来防止session-fixation protection attack,以及限制同一用户开启多个会话的数量

与登录认证拦截时作用一样,持久化用户登录信息,可以保存到session中,也可以保存到cookie或者redis中。

     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
} request.setAttribute(FILTER_APPLIED, Boolean.TRUE); if (!securityContextRepository.containsContext(request)) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication(); if (authentication != null && !trustResolver.isAnonymous(authentication)) {
// The user has been authenticated during the current request, so call the
// session strategy
try {
sessionAuthenticationStrategy.onAuthentication(authentication,
request, response);
}
catch (SessionAuthenticationException e) {
// The session strategy can reject the authentication
logger.debug(
"SessionAuthenticationStrategy rejected the authentication object",
e);
SecurityContextHolder.clearContext();
failureHandler.onAuthenticationFailure(request, response, e); return;
}
// Eagerly save the security context to make it available for any possible
// re-entrant
// requests which may occur before the current request completes.
// SEC-1396.
securityContextRepository.saveContext(SecurityContextHolder.getContext(),
request, response);
}
else {
// No security context or authentication present. Check for a session
// timeout
if (request.getRequestedSessionId() != null
&& !request.isRequestedSessionIdValid()) {
if (logger.isDebugEnabled()) {
logger.debug("Requested session ID "
+ request.getRequestedSessionId() + " is invalid.");
} if (invalidSessionStrategy != null) {
invalidSessionStrategy
.onInvalidSessionDetected(request, response);
return;
}
}
}
} chain.doFilter(request, response);
}

2.2.9 /index.html at position 9 of 10 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'

异常拦截,其处在Filter链后部分,只能拦截其后面的节点并且只处理AuthenticationException与AccessDeniedException两个异常。

AuthenticationException指的是未登录状态下访问受保护资源,AccessDeniedException指的是登陆了但是由于权限不足(比如普通用户访问管理员界面)。

     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; try {
// 直接执行后面的filter,并捕获异常
chain.doFilter(request, response); logger.debug("Chain processed normally");
}
catch (IOException ex) {
throw ex;
}
catch (Exception ex) {
// 从异常堆栈中提取SpringSecurityException
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain); if (ase == null) {
ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
AccessDeniedException.class, causeChain);
} if (ase != null) {
// 处理异常
handleSpringSecurityException(request, response, chain, ase);
}
else {
// Rethrow ServletExceptions and RuntimeExceptions as-is
if (ex instanceof ServletException) {
throw (ServletException) ex;
}
else if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
} // Wrap other Exceptions. This shouldn't actually happen
// as we've already covered all the possibilities for doFilter
throw new RuntimeException(ex);
}
}
}

在这个catch代码中通过从异常堆栈中捕获到Throwable[],然后通过handleSpringSecurityException方法处理异常,在该方法中只会去处理AuthenticationException和AccessDeniedException异常。

     private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {

         if (exception instanceof AuthenticationException) {
// 认证异常,由sendStartAuthentication方法发起认证过程
logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
} else if (exception instanceof AccessDeniedException) {
// 访问权限异常
if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point", exception);
// 匿名用户重定向到认证入口点执行认证过程
sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException("Full authentication is required to access this resource"));
} else {
// 拒绝访问,由accessDeniedHandler处理,response 403
logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);
accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
}
}
} protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
// SEC-112: Clear the SecurityContextHolder's Authentication, as the existing Authentication is no longer considered valid
// 将SecurityContext中的Authentication置为null
SecurityContextHolder.getContext().setAuthentication(null);
// 在调用认证前先将request保存到session
requestCache.saveRequest(request, response);
logger.debug("Calling Authentication entry point.");
// 重定向到认证入口点执行认证
authenticationEntryPoint.commence(request, response, reason);
}

2.2.10 /index.html at position 10 of 10 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'

这个filter用于授权验证。FilterSecurityInterceptor的工作流程引用一下,可以理解如下:FilterSecurityInterceptor从SecurityContextHolder中获取Authentication对象,然后比对用户拥有的权限和资源所需的权限。前者可以通过Authentication对象直接获得,而后者则需要引入我们之前一直未提到过的两个类:SecurityMetadataSource,AccessDecisionManager。

     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
} public void invoke(FilterInvocation fi) throws IOException, ServletException {
if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null) && observeOncePerRequest) {
// filter already applied to this request and user wants us to observe
// once-per-request handling, so don't re-do security checking
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} else {
// first time this request being called, so perform security checking
if (fi.getRequest() != null) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
} InterceptorStatusToken token = super.beforeInvocation(fi); try {
// 如果后面还有filter则继续执行
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
// 保存securityContext
super.finallyInvocation(token);
} super.afterInvocation(token, null);
}
} protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
final boolean debug = logger.isDebugEnabled(); if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException(
"Security invocation attempted for object "
+ object.getClass().getName()
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
+ getSecureObjectClass());
} // 获取配置的权限属性
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object); if (attributes == null || attributes.isEmpty()) {
if (rejectPublicInvocations) {
throw new IllegalArgumentException(
"Secure object invocation "
+ object
+ " was denied as public invocations are not allowed via this interceptor. "
+ "This indicates a configuration error because the "
+ "rejectPublicInvocations property is set to 'true'");
} if (debug) {
logger.debug("Public object - authentication not attempted");
} publishEvent(new PublicInvocationEvent(object)); return null; // no further work post-invocation
} if (debug) {
logger.debug("Secure object: " + object + "; Attributes: " + attributes);
} if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage(
"AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"),
object, attributes);
} // 获取Authentication,如果没有进行认证则认证后返回authentication
Authentication authenticated = authenticateIfRequired(); // Attempt authorization
try {
// 使用voter决策是否拥有资源需要的权限
this.accessDecisionManager.decide(authenticated, object, attributes);
} catch (AccessDeniedException accessDeniedException) {
// 捕获到异常继续上抛
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException));
throw accessDeniedException;
} if (debug) {
logger.debug("Authorization successful");
} if (publishAuthorizationSuccess) {
publishEvent(new AuthorizedEvent(object, attributes, authenticated));
} // Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
attributes); if (runAs == null) {
if (debug) {
logger.debug("RunAsManager did not change Authentication object");
} // no further work post-invocation
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
attributes, object);
}
else {
if (debug) {
logger.debug("Switching to RunAs Authentication: " + runAs);
} SecurityContext origCtx = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
SecurityContextHolder.getContext().setAuthentication(runAs); // need to revert to token.Authenticated post-invocation
return new InterceptorStatusToken(origCtx, true, attributes, object);
}
}

参考文章:

https://blog.coding.net/blog/Explore-the-cache-request-of-Security-Spring

http://blog.didispace.com/xjf-spring-security-4/

http://blog.csdn.net/benjamin_whx/article/details/39204679

spring security 4 filter 顺序及作用的更多相关文章

  1. 解决Spring Security自定义filter重复执行问题

    今天做项目的时候,发现每次拦截器日志都会打两遍,很纳闷,怀疑是Filter被执行了两遍.结果debug之后发现还真是!记录一下这个神奇的BUG! 问题描述 项目中使用的是Spring-security ...

  2. Spring Security 入门(1-6-2)Spring Security - 内置的filter顺序、自定义filter、http元素和对应的filterChain

    Spring Security 的底层是通过一系列的 Filter 来管理的,每个 Filter 都有其自身的功能,而且各个 Filter 在功能上还有关联关系,所以它们的顺序也是非常重要的. 1.S ...

  3. Spring Security(09)——Filter

    目录 1.1     Filter顺序 1.2     添加Filter到FilterChain 1.3     DelegatingFilterProxy 1.4     FilterChainPr ...

  4. Spring Security学习笔记

    Spring Web Security是Java web开发领域的一个认证(Authentication)/授权(Authorisation)框架,基于Servlet技术,更确切的说是基于Servle ...

  5. Spring Security 入门原理及实战

    目录 从一个Spring Security的例子开始 创建不受保护的应用 加入spring security 保护应用 关闭security.basic ,使用form表单页面登录 角色-资源 访问控 ...

  6. Spring Security 梳理 - DelegatingFilterProxy

    可能你会觉得奇怪,我们在web应用中使用Spring Security时只在web.xml文件中定义了如下这样一个Filter,为什么你会说是一系列的Filter呢?    <filter> ...

  7. spring security基本知识(三) 过滤详细说明

    在我们前面的文章Spring Security 初识(一)中,我们看到了一个最简单的 Spring Security 配置,会要求所有的请求都要经过认证.但是,这并不是我们想要的,我们通常想自定义应用 ...

  8. Spring Security 04

    转至:Elim的博客http://elim.iteye.com/blog/2161648 Filter Porxy DelegatingFilterProxy DelegationFilterProx ...

  9. 基于Spring Security 的JSaaS应用的权限管理

    1. 概述 权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源.资源包括访问的页面,访问的数据等,这在传统的应用系统中比较常见.本文介绍的则是基于Saas系统 ...

随机推荐

  1. windows下安装boost库

    工作中现在会接触boost,所以我计划两个月之内努力熟悉一下boost.今天在自己win10系统上尝试安装了boost库,下面把遇到的问题总结一下: 1. 下好1.61版本库,在boost目录下运行b ...

  2. Partitioning &amp; Archiving tables in SQL Server (Part 2: Split, Merge and Switch partitions)

    Reference: http://blogs.msdn.com/b/felixmar/archive/2011/08/29/partitioning-amp-archiving-tables-in- ...

  3. linux进程状态

    系统维护的时候难免会遇到进程的状态的查询和管理,到底什么是R,有的是S,有的还是S+呢?一直有些混沌的问题,今天细细的来总结一下: ps是用来报告系统中程序执行状况的命令这个是无可厚非的,linux进 ...

  4. hibernate-search-5.1.1简易使用

    系统要求java7和hibernate-core-4.3.8,此外还依赖如下jar包 使用demo如下: package com.ciaos.controller; import java.io.IO ...

  5. python学习笔记——第三章 串

    第三章 字符串学习 1.字符串不灵活, 它不能被分割符值 >>> format = "hello, %s. %s enough for ya?" >> ...

  6. arc的安装

    安装:   # sudo apt-get install php5 php5-curl   # ubuntu 系统 # sudo yum install php5 # centos 系统   # cd ...

  7. 【django之admin,单例模式】

    一.admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你可以在项目的 settings.py 中的 INSTAL ...

  8. Python&#160;基于Python实现的ssh兼sftp客户端(上)

    基于Python实现的ssh兼sftp客户端   by:授客 QQ:1033553122 实现功能 实现ssh客户端兼ftp客户端:实现远程连接,执行linux命令,上传下载文件 测试环境 Win7 ...

  9. io重定向打开关闭 Eclipse中c开发printf无法输出解决办法

    if(freopen("e:\\lstm-comparec\\lstm\\lstm\\output.txt","a",stdout)==NULL)fprintf ...

  10. Solidity字符串类型

    字符串可以通过""或者''来表示字符串的值,Solidity中的string字符串不像C语言一样以\0结束,比如abcd这个字符串的长度就为我们所看见的字母的个数,它的长度为4. ...