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. linux(CENTOS)系统各个目录的作用详解

    Linux(CentOS)系统各个目录的作用详解 文件的类型 LINUX有四种基本文件系统类型:普通文件.目录文件.连接文件和特殊文件,可用file命令来识别. 普通文件:如文本文件.C语言元代码.S ...

  2. linux下python2升级python3,python2和python3并存

    wget https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tgz 解压:tar -xzvf Python-3.6.4.tgz cd Pytho ...

  3. 浅析Entity Framework Core2.0的日志记录与动态查询条件

    前言 Entity Framework Core 2.0更新也已经有一段时间了,园子里也有不少的文章.. 本文主要是浅析一下Entity Framework Core2.0的日志记录与动态查询条件 去 ...

  4. 转:C++与JAVA语言区别

    转自:http://club.topsage.com/thread-265349-1-1.html Java并不仅仅是C++语言的一个变种,它们在某些本质问题上有根本的不同: (1)Java比C++程 ...

  5. PHP开发api接口安全验证

    php的api接口 在实际工作中,使用PHP写api接口是经常做的,PHP写好接口后,前台就可以通过链接获取接口提供的数据,而返回的数据一般分为两种情况,xml和json,在这个过程中,服务器并不知道 ...

  6. 腾讯云中ssL证书的配置安装

    https://cloud.tencent.com/document/product/619/12797 配置 Nginx 和 HTTPS 完成以上准备工作,就要开始配置 Nginx 和 HTTPS ...

  7. linux服务器,svn认证失败,配置问题,防火墙等等

    之前自己还真没设置过SVN,今天亲自动手,错误百出,真是够头疼的.在网上随便找了一篇文章,就按照文章介绍开始安装.怎么安装和设置我就不说了,这里主要记录遇到的问题. 1.不知道该怎么设置 svn:// ...

  8. PHP defined() 函数

    定义和用法 defined() 函数检查某常量是否存在. 若常量存在,则返回 true,否则返回 false. 语法 defined(name) 参数 描述 name 必需.规定要检查的常量的名称. ...

  9. SSL和SSH有什么区别

    SSL 是一种安全协议,它为网络(例如因特网)的通信提供私密性.SSL 使应用程序在通信时不用担心被窃听和篡改. SSL 实际上 是共同工作的两个协议:"SSL 记录协议"(SSL ...

  10. 【开发技术】Xcode3与xcode4.2模板对比(Xcode4.2开发之一些变化)

    Xcode3中IOS下的Application的模板如下: Navigation_Based Application OpenGL ES Application Tab Bar Application ...