• Spring Security的架构与实现
    • 概述
      • 运行时环境
      • 核心组件
        • SecurityContextHolder, SecurityContext和Authentication对象
          • 获取当前用户的相关信息
        • UserDetailsService
        • GrantedAuthority
        • 小结
      • 认证
        • 在Spring Security中,什么是认证
        • 直接设置SecurityContextHolder的内容
      • web应用中的认证
        • ExceptionTranslationFilter
        • AuthenticationEntryPoint
        • 认证机制
        • 在请求之间存储SecurityContext
      • Spring Security中的访问控制(授权)
        • AOP Advice和安全
        • AbstractSecurityInterceptor与安全对象
          • 什么是配置属性
          • RunAsManager
          • AfterInvocationManager
          • 扩展安全对象模型
      • 本地化
    • 核心服务
      • AuthenticationManager, ProviderManager 和 AuthenticationProvider
        • 清除成功认证的Credentials
        • DaoAuthenticationProvider
      • UserDetailsService的实现
        • In-Memory Authentication
        • JdbcDaoImpl
          • 权限组
      • 密码编码
        • 什么是哈希
        • hash加盐
        • 哈希和认证
      • 支持Jackson

Spring Security的架构与实现

在熟悉了Spring Security的配置和运行了一些应用之后,你肯定想要进一步了解整个框架是如何运行的。像大多数软件一样,Spring Security框架中有一些常用的接口、类和抽象概念。接下来我们将要研究它们是如何协同工作来支持认证和访问控制的。

概述

运行时环境

Spring Security 3.0需要Java 5.0及以上的版本。因为Spring Security旨在独立运行,所以不需要添加任何特殊的配置文件到运行时环境中。也不需要配置特殊的JAAS或者将Spring Security放到classpath下。

如果使用的是EJB容器或者Servlet容器,也不需要任何特殊的配置文件,或者引入Spring Security。所有需要的文件都包含在应用程序中。

这种设计提供了最大的部署灵活性,因为可以将应用的包复制到任何系统并立即运行。

核心组件

在Spring Security 3.0中,spring-security-core jar包进行了最大程度的精简。不再包含任何与web应用安全、LDAP或者命名空间配置相关的代码。接下来将看到一些核心模块中的Java类型,它们代表着框架的骨架,如果想进行命名空间配置之外的深度开发,需要先弄明白它们是什么(虽然实际上不需要直接与这些类交互)。

SecurityContextHolder, SecurityContext和Authentication对象

最基础的对象就是SecurityContextHolder,其中存储了应用的安全上下文,包含最近使用应用的 principal。默认情况下,使用ThreadLocal存储这些信息,也就是说,同一个线程中的方法可以使用此线程的安全上下文,即使上下文没有作为参数显式传递到方法中。用这种方式使用ThreadLocal是相当安全的,因为在处理当前principal的请求之后清除线程。Spring Security会自动处理这些工作。

principal,通常是指一个用户、设备或者其他可以与应用交互的系统。

有些应用由于用特殊的方式使用了线程所以不适合使用ThreadLocal。例如,Swing 客户端希望所有线程使用同一个安全上下文。SecurityContextHolder支持在启动时配置策略,指定存储上下文的方式。独立应用可以使用SecurityContextHolder.MODE_GLOBAL策略。其他应用可能希望安全线程创建的线程也具有相同的安全特性,可以使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL。有两种方式可以将默认的SecurityContextHolder.MODE_THREADLOCAL模式转换为其他模式:设置系统属性;调用SecurityContextHolder的静态方法。大部分应用不需要改变默认模式,但是如果有需要,可以查阅SecurityContextHolder的JavaDoc。

获取当前用户的相关信息

SecurityContextHolder中存储了当前与应用交互的principal的详细信息。Spring Security使用一个Authentication对象来表示这个信息。通常情况下不需要自己创建Authentication,但可能需要获取它。在应用的任何地方,使用以下代码块来获取当前认证用户的名字:

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}

调用getContext()返回的对象是SecurityContext接口的一个实例,这个对象就是保存在线程中的。接下来将看到,Spring Security中的认证大都返回一个UserDetails的实例作为principal。

UserDetailsService

上面的代码块中,还有一个需要注意的地方就是,可以从Authentication对象中获取principal。大多数情况下可以转换成UserDetails对象。UserDetails是Spring Security中的一个核心接口。它表示一个principal,但是是可扩展的、特定于应用的。可以认为UserDetails是数据库中用户表记录和Spring Security在SecurityContextHolder中所必须信息的适配器。当它表示数据库中用户表的记录的时候,通常会将UserDetails转换成应用中提供的原始对象,以便于调用某些业务方法(例如:getEmail(),getEmployeeNumber()等等)。
现在,你肯能会问,应该什么时候提供一个UserDetails对象呢?如何提供呢?以上所说的都是声明式的,不必编写任何Java代码,其中发生了什么呢?最简单的回答是,有一个特殊的接口UserDetailsService。这个接口中仅有的一个方法接收一个String类型的用户名参数,返回UserDetails:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

在Spring Security中,这是获取用户信息最常用的方法,这个方法遍布在框架中,只要框架需要用户信息,就会使用这个方法。
如果认证成功,UserDetails会被用来创建Authentication对象,这个对象存放在SecurityContextHolder中(有关详情,查阅)。Spring Security提供了许多UserDetailsSerivice接口的实现,包括使用内存中map的实现(InMemoryDaoImpl)和使用JDBC的实现(JdbcDaoImpl)。但开发者更喜欢自己编写,在已存在的表示员工、客户、或其他应用用户的DAO上实现。记住不管自定义UserDetailsService返回什么,都可以用上面的代码块从SecurityContextHolder中获取。

对于UserDetailsService经常会有一些困惑。它是纯粹的用户数据的DAO,除了将用户数据提供给框架内的其他组件,没有任何其他功能。

GrantedAuthority

除了principal,Authentication提供的另外一个比较重要的方法是getAuthorities()。这个方法提供一个GrantedAuthority对象的数组。GrantedAuthority是授权给某个principal的权限。这些权限通常是角色,例如:ROLE_ADMINISTRATOR或者ROLE_HR_SUPERVISOR。稍后将为这些角色配置web权限,方法权限和域对象权限。Spring Security能解析这些权限,并希望出现这些权限。GrantedAuthority对象通常由UserDetailsService加载。
通常情况下,GrantedAuthority对象是应用范围的权限。没有特定于给定的域对象。所以,不要用一个GrantedAuthority对象来表示工号为54的Employee对象,这样依赖会有成千上万个这样的权限,最终导致内存耗尽(在最好的情况下,也会使得应用在认证上耗费很长的时间)。 Spring Security专门设计用于处理此类需求,但应该使用项目的域对象安全性来实现此功能。

小结

目前为止,已经讲解了下面这些Spring Security的主要模块:
- SecurityContextHolder,用于获取SecurityContext
- SecurityContext,存放了Authentication和特定于请求的安全信息。
- Authentication,特定于Spring Security的principal。
- GrantedAuthority,对某个principal的应用范围内的授权许可。
- UserDetail,提供从应用程序的DAO或其他安全数据源构建Authentication对象所需的信息。
- UserDetailsService,接受String类型的用户名,创建并返回UserDetail

现在已经了解了这些安全组件,接下来看看认证的流程。

认证

Spring Security适用于许多不同的认证环境中。虽然建议使用Spring Security进行身份验证,而不是与原有的容器管理身份验证集成,但仍然支持与原有身份验证系统集成。

在Spring Security中,什么是认证

考虑一个大家都熟悉的认证场景:
1. 提示用户使用用户名和密码登录。
2. 系统验证此用户的密码正确。
3. 获取该用户的上下文信息(角色等等)。
4. 用户继续操作,可能执行一些受访问控制机制保护的操作根据当前安全上下文信息检查该操作所需要的许可。

前三项构成了安全认证过程,因此接下来将了解在Spring Security中这些步骤是如何进行的。
1. 获取用户名和密码,并构建一个UsernamePasswordAuthenticationToken实例(之前提到过的Authentication接口的一个实例)。
2. token传递给AuthenticationManager实例进行验证。
3. AuthenticationManager在成功验证后返回填充好的Authentication实例。
4. 传入返回的authentication实例,通过调用SecurityContextHolder.getContext().setAuthentication(…)方法构建SecurityContext。

从这个时候开始,用户就是认证过的了。下面是代码示例:

import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;public class AuthenticationExample {
private static AuthenticationManager am = new SampleAuthenticationManager();public static void main(String[] args) throws Exception {BufferedReader in = new BufferedReader(new InputStreamReader(System.in));while(true) {System.out.println("Please enter your username:");String name = in.readLine();System.out.println("Please enter your password:");String password = in.readLine();try {Authentication request = new UsernamePasswordAuthenticationToken(name, password);Authentication result = am.authenticate(request);SecurityContextHolder.getContext().setAuthentication(result);break;} catch(AuthenticationException e) {System.out.println("Authentication failed: " + e.getMessage());}}System.out.println("Successfully authenticated. Security context contains: " +SecurityContextHolder.getContext().getAuthentication());
}
}class SampleAuthenticationManager implements AuthenticationManager {
static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();static {AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
}public Authentication authenticate(Authentication auth) throws AuthenticationException {if (auth.getName().equals(auth.getCredentials())) {return new UsernamePasswordAuthenticationToken(auth.getName(),auth.getCredentials(), AUTHORITIES);}throw new BadCredentialsException("Bad Credentials");
}
}

这里编写了一个小程序,要求用户输入用户名和密码然后顺序往下执行。这里实现的AuthenticationManager会认证任何用户名和密码相同的用户。并赋予每个用户一个角色。这个程序的输出如下:

Please enter your username:
bob
Please enter your password:
password
Authentication failed: Bad Credentials
Please enter your username:
bob
Please enter your password:
bob
Successfully authenticated. Security context contains: \
org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d0230: \
Principal: bob; Password: [PROTECTED]; \
Authenticated: true; Details: null; \
Granted Authorities: ROLE_USER

通常不需要写这样的代码。认证过程会在内部执行,例如web认证的filter。这里展示这段代码回答了“在Spring Security中真正构成认证过程的是什么”这个问题。当SecurityContextHolder包含一个完全填充的authentication对象时,这个用户就是经过认证的。

直接设置SecurityContextHolder的内容

实际上,Spring Security不关心如何将Authentication对象放到SecurityContextHolder中。唯一的要求是,SecurityContextHolder中要存储Authentication。表示AbstractSecurityInterceptor(详情见下文)需要授权用户操作之前的principal。

可以编写自己的过滤器或MVC控制器,提供与不基于Spring Security的身份验证系统的互操作性。仅需要编写一个filter从本地读取第三方用户信息,构建Spring Security的Authentication对象,并将其放置到SecurityContextHolder中。如果编写了自定义的filter,还需要考虑认证架构中自动处理掉的一些事情。比如,在响应之前,需要先创建一个HttpSession来缓存请求之间的上下文(响应提交之后不能创建session)。

如果想要了解AuthenticationManager实现的真实示例,可以查阅核心服务章节。

web应用中的认证

现在研究一下在web应用中使用Spring Security的情况(不用web.xml启用security)。用户如何认证?安全上下文如何构建?

考虑一个典型的web应用认证过程:
1. 访问主页,点击一个链接。
2. 服务器收到请求,服务器确定需要一些受保护的资源。
3. 因为还未认证,服务器返回一个响应,要求用户进行认证。这个响应可以是一个Http返回码,也可以重定向到一个特殊的web页面。
4. 取决于认证机制,浏览器会重定向到一个特殊的web页面,这个页面可以填写表单;或者浏览器会以某种方式获取你的身份(通过BASIC anthencitation对话框、cookie、或者X.509 certificate)。
5. 浏览器回送响应到服务器。这可能是一个包含表单中填写信息的HTTP POST请求,或者包含认证信息的HTTP头。
6. 服务器会验证当前的凭证是否是有效的。如果是,会到下一步。如果不是,浏览器会重新进行认证步骤(回到步骤2)。
7. 触发认证过程的原始请求会重试。如果有足够的权限,请求会成功。否则,会收到返回的HTTP Code 403,也就是说禁止访问。

对于以上的大多数步骤,Spring Security都有不同的类负责。主要有(按照被使用的顺序):ExceptionTranslationFilterAuthenticationEntryPoint,和负责调用上文提到过的AuthenticationManager的“authentication mechanism”。

ExceptionTranslationFilter

ExceptionTranslationFilter是一个Spring Security filter,负责侦测所有抛出的Spring Security异常。通常授权服务的主要提供者AbstractSecurityInterceptor会抛出这些异常。下一章会讨论AbstractSecurityInterceptor,但现在只需要知道它会抛出java异常但不了解任何HTTP或者如何认证一个principal。ExceptionTranslationFilter提供了这些服务,并负责返回错误码403(如果principal已经经过认证,但没有足够的访问权限,参照上面的第7步),或者启动AuthenticationEntryPoint(如果principal尚未认证,将回到第三步)。

AuthenticationEntryPoint

AuthenticationEntryPoint负责列表中的第三步。可以想象,每个web应用都有默认的认证策略。主要的身份验证系统都有自己的AuthenticationEntryPoint实现,以执行第三步中的动作。

认证机制

一旦浏览器提交了认证凭证(Http表单post请求或者Http头),服务器需要收集这些认证信息。现在到了第六步。在Spring Security中,对从用户端(通常是浏览器)收集用户认证信息的方法有一个特殊的名字,称作“认证机制”。例如基于表单的登录和BASIC认证。当从用户端收集到认证信息后,就会构建Authentication请求对象,并交给AuthenticationManager

当认证机制返回完全填充的Authentication对象,则表示请求是有效的,将Authentication对象放到SecurityContextHolder中,并触发重试原始的请求(第七步)。如果AuthenticationManager拒绝这个请求,认证机制会要求用户端重新从第二步开始。

在请求之间存储SecurityContext

根据应用程序的类型,可能需要采用策略来在用户操作之间存储安全上下文。在典型的Web应用程序中,用户登录过后,会由其session ID标识。服务器在会话时间内缓存principal信息。在Spring Security中,在请求之间存储SecurityContext的工作由SecurityContextPersistenceFilter负责,默认将上下文存储为HTTP请求之间的HttpSession。它为每个请求恢复上下文到SecurityContextHolder中,更重要的是,当请求完成时还会清除SecurityContextHolder。出于安全目的,不应该直接与HttpSession交互。通常会使用SecurityContextHolder


许多其他类型的应用(例如无状态的RESTful web服务)不使用Http Session,且为每一个请求都重新认证。但是,在filter chain中引入SecurityContextPersistenceFilter来确保每个请求之后都清除了SecurityContextHolder仍然很重要。

在单个session中接受并发请求的应用中,同一个SecurityContext实例会被线程共享。虽然使用了ThreadLocal,但每个请求从HttpSession中获取到的都是同一个实例。如果使用SecurityContextHolder.getContext(),并在返回的上下文对象上调用setAuthentication(anAuthentication)方法,那么Authentication对象会在共享同一个SecurityContext实例的并发线程中修改。可以自定义SecurityContextPersistenceFilter的行为来创建为每个请求创建一个全新的SecurityContext,防止一个线程中的变动影响到其他线程。或者改动上下文的时候创建一个新的实例。SecurityContextHolder.createEmptyContext()方法始终返回一个全新的上下文实例。

Spring Security中的访问控制(授权)

在Spring Security中,主要负责做出授权决定的就是AccessDecisionManager。它有一个decide方法接受一个代表principal请求访问Authentication的对象,一个“安全对象”(见下文)和一个适用于该对象的安全元数据属性列表(例如访问需要的授权角色)。

AOP Advice和安全

如果熟悉AOP,就会知道这几种不同类型的advice:before,after,throws和around。around advice非常有用,因为advisor可以选择是否继续进行方法的调用,是否修改返回值,是否抛出异常。Spring Security为方法调用和web请求提供了around advice。Spring Security使用了Spring标准的APO支持来为方法执行进行around advice,还通过标准的filter为web请求实现around advice。

如果不熟悉AOP,知道Spring Security可以帮助保护方法执行和web请求就行了。大部分人对于在service层保护方法的执行更感兴趣。因为在当前Java EE应用,service层是大多数业务逻辑所在。如果只需要保护service层的方法执行,Spring AOP就足够了。如果需要直接保护域对象,可以考虑一下AspectJ。

可以选择通过ApectJ或Spring AOP执行方法授权,或者通过使用filter来执行web请求的授权。还可以将这些方法组合起来。主流的使用模式是执行一些web请求授权,和一些service层Spring AOP的方法执行授权。

AbstractSecurityInterceptor与安全对象

什么是“安全对象”?Spring Security使用这个术语来表示任何可以应用安全保护(例如授权)的对象。最常见的例子是方法执行和web请求。

每个被支持的安全对象类型都有自己的拦截器类,这些拦截器类都是AbstractSecurityInterceptor的子类。当AbstractSecurityInterceptor被调用的时候,如果principal已经认证了,SecurityContextHolder会存储一个有效的Authentication

AbstractSecurityInterceptor为处理安全对象提供了一致的工作流程:
1. 查找与当前请求关联的“配置属性”
2. 将安全对象,当前Authentication和配置属性提交给到AccessDecisionManager以进行授权决策
3. 在调用时改变Authentication(可选)
4. 允许安全对象调用继续(如果访问被授权)
5. 调用返回后,如果有配置,就调用AfterInvocationManager。如果调用抛出异常,AfterInvocationManager不会执行。

什么是配置属性

“配置属性”可以被认为是对于被AbstractSecurityInterceptor使用的类有特殊意义的字符串。在框架中由ConfigAttribute接口表示。取决于AccessDecisionManager实现的复杂程度,它可能是简单的角色名或更复杂的含义。AbstractSecurityInterceptor配置了一个SecurityMetadataSource,这个对象可以用来查找某个安全对象的属性。通常这个配置是对用户隐藏的。配置属性会作为被保护方法的注解或者被保护URL的访问属性加入。例如,当在命名空间介绍中看到类似<intercept-url pattern='/secure/' access='ROLE_A,ROLE_B'/>的时候,意味着配置属性ROLE_AROLE_B适用于给定模式匹配的web请求。在实际开发中,使用默认AccessDecisionManager配置,意味着,任何人的GrantedAuthority,只要匹配这两个属性中的任何一个,就会被允许访问。严格来说,它们仅仅是属性而已,并且它们的解释取决于AccessDecisionManager的实现。前缀ROLE_表示这些属性是角色,应该由Spring Security的RoleVoter使用。这仅在使用基于voter的AccessDecisionManager时才会有效,AccessDecisionManager如何实现在授权章节中。

RunAsManager

假设AccessDecisionManager允许请求,AbstractSecurityInterceptor通常只会继续这个请求。话虽如此,但在某些极端情况下,用户可能希望使用不同的Authentication替换SecurityContext中的Authentication,这由AccessDecisionManager调用RunAsManager处理。这在合理的极端情况下是有用的,例如某个服务层方法需要以不同的身份调用远程系统。因为Spring Security自动将安全身份标识自动从某个服务器传播到其他服务器(假设使用了正确配置的RMI活着HttpInvoker远程协议客户端),这可能很有用。

AfterInvocationManager

安全方法调用继续并返回,这可能意味着某个方法完成了或者某个filter链的继续,AbstractSecurityInterceptor获得最后的机会来处理调用。此阶段,AbstractSecurityInterceptor可能会修改返回对象。我们可能希望这种情况发生,因为无法在安全对象调用的“途中”进行授权决策。为了高度可拔插性,AbstractSecurityInterceptor会将控制权传递给AfterInvocationManager,以便在需要时修改对象。这个类甚至能完全替换掉对象,或者抛出异常,或者不以任何方式改变它。只有在调用成功后才会执行调用后检查。如果有任何的异常,这个检查会被跳过。

AbstractSecurityInterceptor及其相关对象如图所示。

image

扩展安全对象模型

只有开发人员考虑采用全新的拦截和授权请求方式才需要直接使用安全对象。例如,可以构建新的安全对象以保护对消息系统的调用。任何需要安全保护并且支持调用拦截(如AOP的around advice语义)的事物都能够成为安全对象。但大多数Spring应用只使用当前完全透明的支持的三种安全对象类型(AOP Alliance MethodInvocation,AspectJ JoinPoint和Web请求FilterInvocation)。

本地化

核心服务

现在已经概览了Spring Security体系结构及其核心类,接下来仔细研究几个核心接口机器实现,特别是AuthenticationManager,UserDetailsServiceAccessDecitionManager

AuthenticationManager, ProviderManager 和 AuthenticationProvider

AuthenticationManager只是一个接口,因此实现可以任意选择,但它在实践中如何工作呢?如果我们需要检查多个认证数据库或不同认证服务(如数据库和LDAP服务器)的组合,该怎么办?

在Spring Security中默认实现是ProviderManager,但它并没有自己处理认证请求,而是代理给了配置的一组AuthenticationProdicer,依次查询它们是否能进行认证。每个provider将抛出异常或返回完全填充的Authentication对象。还能想起来UserDetailsUserDetailsService吗?如果想不起来的话,回头看一看上一章中的相关内容吧。验证一个认证请求最常见的方法是加载正确的UserDetails并检查根据加载的密码检查用户输入的密码。DaoAuthenticationProvider使用的就是这个方法(见下文)。加载的UserDetails对象特别是它所包含的GrantedAuthority,会在构建完全填充的Authentication对象时用到,这个Authentication对象在验证成功时被返回,并存储到SecurityContext中。

如果使用命名空间,会在内部创建和维护一个ProviderMaanger,可以通过authentication命名空间在xml中配置添加provider。在这种情况下,不应该在应用上下文中自己声明ProviderManagerbean。然而,如果没有使用authentication命名空间配置,可以如下声明:

<bean id="authenticationManager"class="org.springframework.security.authentication.ProviderManager"><constructor-arg><list><ref local="daoAuthenticationProvider"/><ref local="anonymousAuthenticationProvider"/><ref local="ldapAuthenticationProvider"/></list></constructor-arg>
</bean>

在上面的实例中有三个provider。它们按照出现的顺序执行(使用List表示),每个provider都进行认证,或者返回null跳过认证。如果所有实现都返回null,ProviderManager会抛出一个ProviderNotFountException。如果对链接provider感兴趣,可以查阅ProviderManager的JavaDoc。

诸如web表单登录处理filter之类的认证机制,会被注入ProviderManager的引用,并调用它来处理认证请求。需要的的provider提供程序有时可以与身份验证机制互换,而在其他时候,它们将依赖于特定的身份验证机制。例如,DaoAuthenticationProviderLdapAuthenticationProvider兼容任何提交用户名、密码认证请求的机制,因此可以使用基于表单的登录或HTTP Basic 认证。另一方面,有些认证机制创建只能被某一类型的AuthenticationProvicer处理的认证请求对象。一个例子是JA-SIG CAS,它使用服务票据的概念,因此只能由CasAuthenticationProvider进行身份验证。不不要太在意这个问题,因为如果忘记注册合适的provider,尝试进行认证的时候会抛出ProviderNotFoundException

清除成功认证的Credentials

默认情况下(从Spring Security 3.0开始),ProviderManager会尝试从成功认证请求返回的Authentication对象中清除敏感的从credentials信息。这防止了类似密码这样的敏感信息的不必要的保留。

但是在使用用户对象缓存的时候,可能会有问题,例如,在无状态的应用中提升性能。如果Authentication包含缓存中某个对象的引用(例如一个UserDetails实例)并且这个对象的credentials已经移除,则无法再对缓存的值进行认证。如果使用了缓存,需要注意这一点。要解决这个问题可以在缓存实现中或者在创建返回Authentication对象的AuthenticationProvider中创建被引用对象的副本。或者禁用ProviderManager中的eraseCredentialsAfterAuthentication属性。详细信息请参阅Javadoc。

DaoAuthenticationProvider

Spring Security对AuthenticationProvider最简单的实现就是DaoAuthenticationProvider,它也是该框架最早支持的provider之一。其使用UserDetailsService(作为一个DAO)来查找用户名,密码和GrantedAuthority;通过简单地比较提交的UsernamePasswordAuthenticationTokenUserDetailsService加载的密码来认证用户。配置这个provider非常简单:

<bean id="daoAuthenticationProvider"class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>

PasswordEncoder是可选的。PasswordEncoder提供对UserDetailsService返回的UserDetails对象中密码的编码和解码。后面会详细介绍。

UserDetailsService的实现

正如上文所述,绝大多数认证的provider都使用了UserDetailsUserDetailsService接口。回想一下UserDetailsService接口只有一个方法:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

返回的UserDetails是一个接口,提供保证非空的认证信息(例如:用户名、密码、权限以及账户是否启用)的一系列getter方法。大多数的认证provider会使用UserDetailsService,即使用户名和密码实际上不会被用在认证中。它们可能为了获取GrantedAuthority信息而使用返回的UserDetails对象,因为某些其他系统(如LDAP或X.509或CAS等)承担了实际的验证凭证(Credentials )的责任。

鉴于UserDetailsService实现起来非常简单,使用者应该能使用自己的持久层策略来轻松地查询到认证信息。同时,Spring Security也提供了一些有用的基础实现。

In-Memory Authentication

使用自定义的UserDetailsService实现来从持久层引擎中获取信息非常简单,但是许多应用不需要这么复杂。特别是构建prototype应用或者刚开始集成Spring Security,不想要花费过多的时间来配置数据库或者实现UserDetailsService。对于这种情况,可以使用安全命名空间中的user-service元素:

<user-service id="userDetailsService">
<user name="jimi" password="jimispassword" authorities="ROLE_USER,ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE,USER" />
</user-service>

同样也支持外部properties文件:

<user-service id="userDetailsService" properties="users.properties" />

properties文件应该包含如下格式的数据:

username=password,grantedAuthrity[,grantedAuthority][,enabled|disabled]

例如:

jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
bob=bobspassword,ROLE+USER,enabled

JdbcDaoImpl

Spring Security也提供了从JDBC数据源获取认证信息的UserDetaislService实现。内部使用Spring JDBC,因此避免了ORM的复杂性,只用于存储用户信息。如果应用使用了ORM工具,可能需要自定义UserDetailsService来重用已有的映射文件。回到JdbcDaoImpl,示例配置如下所示:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean><bean id="userDetailsService"class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>

可以通过修改上边的DriverManagerDataSource以使用不同的关系型数据库管理系统。还可以与任何其他Spring配置一样使用从JNDI获取的全局数据源。

权限组

默认情况下,JdbcDaoImpl为单个用户加载权限,并假设权限直接映射到用户(请参阅数据库schema附录)。另一种方法是将权限划分为组并将组分配给用户。有些开发人员更喜欢这种方法来管理用户权限。有关如何启用组权限的更多信息,请参阅JdbcDaoImpl的Javadoc。权限组的schema也包含在附录中。

密码编码

Spring Security的PasswordEncoding接口被用来支持密码的使用,密码会在持久化阶段以某种方式编码。绝对不能以纯文本方式存储密码。应该始终使用单向密码哈希算法(例如使用內建盐值的bcrtpy,并且每个密码的盐值都不一样)。不要使用普通的哈希函数,如MD5或SHA,加盐的版本也不能使用。Bcrypt被有意设计为缓慢且防止离线密码破解的,而标准的哈希算法非常快而且很容易用于在某些硬件上并发测试上千个密码。有些开发者认为这不适用于自己的系统,因为密码库很安全而且没有被离线攻击的风险。如果真的这样认为的话,建议了解一下因为不安全的存储密码而受到攻击的知名网站。使用org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder是一个很好的选择。在其他常见的编程语言中也有兼容的实现,因此从互操作性来说它也是一个很好的选择。

如果使用的是已经有哈希密码的系统,则需要使用与当前算法匹配的encoder(至少在将用户迁移到更安全的方案之前,通常这会涉及到要求用户设置新密码,因为hash是不可逆的)。Spring Security有一个包含传统密码编码实现的包,即org.springframework.security.authentication.encoding。可以使用新的(bcrypt)或者旧的PasswordEncoder类型注入到DaoAuthenticationProvider中。

什么是哈希

密码哈希并不是Spring Security独有的,但对这个概念不熟悉的使用者经常会对其非常疑惑。哈希(或摘要)算法是一个单向函数,其从一些输入数据(例如密码)产生一段固定长度的输出数据(哈希)。例如,字符串“password”(十六进制)的MD5哈希值是

5f4dcc3b5aa765d61d8327deb882cf99

在某种意义上,哈希是“单向的”,即在给定哈希值的情况下,计算出原始值或者实际上任何可能产生该哈希值的值是非常困难的(实际上是不可能的)。这个性质使哈希值对于身份验证非常有用。它们可以作为明文密码的替代方法存储在用户数据库中,即使这些值被泄露,也不会立即泄露可用于登录的真实密码。注意,这也意味着无法在编码后恢复密码。

hash加盐

使用密码哈希的一个潜在问题是,如果输入常用字,很容易绕过哈希的单向性。用户通常喜欢设置相似的密码,并且可以在网络上大量获取以前被黑客攻击的网站中的密码字典。例如,如果通过googl搜索hash值5f4dcc3b5aa765d61d8327deb882cf99,很快会找到原始值”password”。同理,攻击者可以构建hash字典来查找原始密码。解决这个问题的一个办法是使用适当健壮的密码策略来防止使用常用字。另外一种方法是在计算hash值时使用“盐”。“盐”是每个用户已知已知数据的附加字符串,在计算hash之前与密码组合。理想情况下,数据应尽可能随机,但实际上任何盐值通常都比没有要好。使用盐值意味着攻击者必须为每个盐值构建一个hash字典,使攻击更加困难(但并非不可能)。

Bcrypt在编码时自动为每个密码生成一个随机盐值,并以标准格式将其存储在bcrypt字符串中。

处理盐值的传统方法是将SaltSource注入到DaoAuthenticationProvider,它将获取特定用户的盐值并将其传递给PasswordEncoder。使用bcrypt意味着使用者不必担心盐值处理的细节(例如存储值的位置),因为它都是在内部完成的。所以我们建议使用bcrypt,除非系统可以单独存储盐值。

哈希和认证

当某个认证provider(例如Spring Security的DaoAuthenticationProvider)需要根据某用户已知信息校验提交的身份验证请求中的密码时,存储的密码是以某种方式编码的,提交上来的密码也需要通过同样的算法编码。Spring Security不能控制持久化的值,所以是否兼容取决于使用者。如果在Spring Security的认证配置中加入了密码哈希,但是在数据库中存储的是明文密码,认证永远都不会成功。考虑另外一种情况,数据库使用MD5对密码进行编码,并且应用配置为使用Spring Security的Md5PasswordEncoder,也可能出现问题。数据库中可能使用的是Base64编码,而encoder使用十六进制字符串(默认值)。或者数据库中使用大写而encoder输出的是小写的。最好是在进行应用认证之前,编写测试代码测试一下对于给定的密码和盐值,配置的密码encoder的输出和数据库中存储的值匹配。使用像bcrypt这样的标准可以避免这些问题。
如果想要直接在java中生成编码后的密码存储到数据库中,可以使用PasswordEncoder中的encode方法。

支持Jackson

Spring Security为持久化Spring Security相关的类提供了对Jackson的支持。这可以提高在使用分布式session(即session复制,SpringSession等)时序列化Spring Security相关类的性能。

如果要使用这个特性,将JacksonJacksonModules.getModules(ClassLoader)注册为Jackson Modules:

ObjectMapper objectMapper = new ObjectMapper();
ClassLoader loader=getClass().getClassLoader();
List<Module> modules=SecurityJackson2Modules.getModules(loader);
mapper.registerModules(modules);// ... use ObjectMapper as normally ...
SecurityContext context = new SecurityContextImpl();
// ...
String json = mapper.writeValueAsString(context);
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 微信小程序WePY框架手册

    WePY 框架概述 WePY 是腾讯官方出品的一个小程序快速开发框架,对原生小程序的开发模式进行了再次封装,更贴近于 MVVM 架构模式,并支持ES6/7的一些新特性,同时语法风格更接近于 Vue.js,使用 WePY 框架能够提高小程序的开发效率。 注意:WePY 只是小程序的快速开发框架之一,…...

    2024/4/16 22:04:17
  2. ubuntu黑屏无法进入系统

    ubuntu 18.04黑屏无法进入系统 表现为:每次开机后,第一次打开VMware,并启动ubuntu 18.04,均正常启动,不会黑屏。 但只要重启该虚拟机,必然会一直处于黑屏状态.无法进入系统。 搜索了很多方法,大家可以自己去找,但对我而言均无用。 尤其是那个“以管理员身份运行cmd控制…...

    2024/4/16 22:03:40
  3. PTA 古风排版 Java

    PTA 古风排版 Javaimport java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.ArrayL…...

    2024/3/30 12:27:04
  4. 依赖倒转原则,接口,关联

    通过接口和关联去编写一个电子书软件,电子书软件可以有不同的类型,例如TXT,doc等,然后怎么通过接口去实现,然后在新建一个用户去看 主要是用了依赖倒转原则interface Ibook{void print(); }class EbookReader{Ibook book;void read(Ibook book){book.print();} }class TX…...

    2024/4/17 19:12:28
  5. 在web项目中spring security与cas 结合使用的意义

    在进行spring security的描述之前,我想描述一下认证与授权两个概念。 针对于企业级web应用中一个登陆过程,需要完成什么功能。其实就是完成认证与授权。在基于form表单的登陆形式,用户进入系统,在这个过程需要完成认证与授权。 所谓认证,就是当用户试图进入系统,而系统…...

    2024/4/16 22:03:52
  6. Initialization failure:0x0000000C

    今天启动QQ时发生了点错误具体什么原因我就不说了,直接说解决方法: 1.找到你的命令提示符(windows键 + r键,输入cmd)找到后以管理员身份运行 以管理员身份运行 以管理员身份运行(重要的事情说三遍)2.在命令行中输入:NETSH WINSOCK RESET CATALOG 之后敲击回车就会提示…...

    2024/4/16 22:03:58
  7. WePY框架、mpvue框架、uniapp以及原生微信小程序的开发有何区别

    WePY框架 WePY是一个由腾讯团队推出的最早的一款类Vue语法的小程序组件化开发框架。WePY2 是基于小程序原生组件实现组件化开发。因此WePY2支持的最低版本小程序基础库为 1.6.3。WePY官网https://wepyjs.github.io/wepy-docs/,WePY的特点有:类Vue开发风格 支持自定义组件开发 …...

    2024/4/27 12:32:20
  8. Spring Security Oauth2:RedisTokenStore(一)之Token内容揭秘

    [本文仅仅作为自我学习的笔记,有什么不对或误人之处,望大佬指正] RedisTokenStore之Token内容揭秘 Token存储方式 Spring Security Oauth2 存储Token的方式有多种, 比如JWT、Jdbc(数据库)、Redis等,根据Oauth2继承类图,实现方式如下:使用Redis存储Token具有明显的优势,…...

    2024/4/16 22:03:40
  9. 2017 C4天梯 L1-039. 古风排版

    题目描述 中国的古人写文字,是从右向左竖向排版的。本题就请你编写程序,把一段文字按古风排版。输入格式:输入在第一行给出一个正整数N(<100),是每一列的字符数。第二行给出一个长度不超过1000的非空字符串,以回车结束。输出格式:按古风格式排版给定的字符串,每列…...

    2024/4/27 0:51:26
  10. 偏好设置

    获得偏好设置方式: 第一种方式:Context中定义的一个方法,getSharedPreferences(偏好设置文件的名称, 偏好设置文件保存的位置);第二种方式:Activity中定义的一个方法,getPreferences(偏好设置文件保存的位置);偏好设置文件的名字是调用该方法的Activity的名字第三种方式:…...

    2024/4/18 15:59:28
  11. VMware虚拟机开机黑屏解决办法

    1、虚拟机开机黑屏,挂起时可以看到显示; 2、在windows系统下输入cmd,找到命令提示符,右键点击命令提示符,则弹出菜单后选择“以管理员身份运行” 3、在命令窗口输入“netsh winsock reset”,若出现成功地重置winsock目录,然后重新启动计算机即可解决。 4、如果还不行,在V…...

    2024/3/31 22:35:49
  12. 微信小程序开发之wepy框架

    wepy是由腾讯团队推出的小程序组件化开发框架,为什么一开始推出的时候不直接用这一套!?官网地址WePY 是一款让小程序支持组件化开发的框架,通过预编译的手段让开发者可以选择自己喜欢的开发风格去开发小程序。框架的细节优化,Promise,Async Functions的引入都是为了能让开…...

    2024/4/24 5:44:05
  13. Java基础(二):继承+多态+抽象类

    文章目录一、子类与父类:二、继承中的成员变量隐藏和方法重写1、成员变量的隐藏:2、子类对继承父类方法的重写:3、重载三、super关键字★1、调用父类的构造方法。super必须在第一行。2、使用super访问被子类成员隐藏的父类成员或者方法。四、final关键字1、修饰类:表示该类…...

    2024/4/16 22:05:16
  14. Spring Security源码分析十六:Spring Security项目实战

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功…...

    2024/4/28 10:23:47
  15. L1-039 古风排版 (20 分)

    L1-039 古风排版 (20 分) 中国的古人写文字,是从右向左竖向排版的。本题就请你编写程序,把一段文字按古风排版。 输入格式: 输入在第一行给出一个正整数N(<100),是每一列的字符数。第二行给出一个长度不超过1000的非空字符串,以回车结束。 输出格式: 按古风格式排版…...

    2024/4/16 22:04:46
  16. 在wepy框架中使用echarts

    echarts-for-wexin目前提供的方式是在原生小程序中的使用,直接将其中的ec-canvas作为组件在wepy的页面中使用可能会出现问题,并不能达到渲染的效果 (具体使用可以到我的这个github项目中查看,页面路径为pages/charts,使用到的相关文件也可直接复制项目中的)我最开始的使用方…...

    2024/4/16 22:05:16
  17. 使用Spring Security进行自动登录验证

    在之前的博客使用SpringMVC创建Web工程并使用SpringSecurity进行权限控制的详细配置方法 中,我们描述了如何配置一个基于SpringMVC、SpringSecurity框架的网站系统。在这篇博客中,我们将继续描述如何使用Spring Security进行登录验证。原文地址:http://www.datalearner.com/…...

    2024/4/26 21:41:41
  18. wepy中的页面跳转

    1.在pages中创建好页面之后,需要在app.wpy中的config中配置好页面路由; 2.如果跳转的按钮在page页面中 this.$navigate({url:"content"})3.如果跳转的按钮在子组件中,有下面两种方法this.$parent.$navigate({url:"content"})wepy.navigateTo({url: /pag…...

    2024/4/17 23:04:18
  19. IBOOKS导入EPUB出现-23错误时解决方法

    问题: IBOOKS导入 .epub 文件时出现 -23 错误解决办法: 1. 将文件后缀名从 .epub修改成 .rar2. 将OPS文件夹中的CSS文件夹删掉3. 将文件后缀名从.rar 改回 .epub 转载于:https://www.cnblogs.com/joshuali/archive/2010/11/13/4339426.html...

    2024/4/16 22:05:04
  20. VM虚拟机经常需要重置网络编辑器以启用NAT的解决办法

    开机启动项里有几个vmware的服务程序不能禁掉,win10为例,在window的计算机上右键,管理--服务--开启VMware NAT Service和DHCP服务,设置为延时启动/自动启动即可。...

    2024/4/20 15:14:05

最新文章

  1. socat移植到arm+linux

    socat是一个用于建立双向数据流传输的工具&#xff0c;它可以在不同的网络层上创建连接&#xff0c;并支持多种协议&#xff0c;如TCP、UDP、SSL等。它非常强大且易于使用&#xff0c;因此广泛用于网络开发和系统管理中&#xff0c;这里记录一下移植到嵌入式系统的过程。 下载s…...

    2024/5/2 22:31:36
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. C# Solidworks二次开发:六种配合方式以及注意事项API详解

    今天要写的文章是关于配合的一些API介绍。 如果大家还不知道创建配合的API用的是哪个&#xff0c;可以看一下我之前写的文章&#xff1a;C# Solidworks二次开发&#xff1a;创建距离配合以及移动组件API详解_solidworks transform2-CSDN博客 &#xff08;1&#xff09;今天要…...

    2024/5/1 10:20:40
  4. 【单调队列】滑动窗口与子矩阵

    一、滑动窗口 给定一个大小为 n≤1e6 的数组。 有一个大小为 k 的滑动窗口&#xff0c;它从数组的最左边移动到最右边。 你只能在窗口中看到 k 个数字。 每次滑动窗口向右移动一个位置。 以下是一个例子&#xff1a; 该数组为 [1 3 -1 -3 5 3 6 7]&#xff0c;k 为 3。 …...

    2024/4/30 2:12:58
  5. TCP网络协议栈和Posix网络部分API总结

    文章目录 Posix网络部分API综述TCP协议栈通信过程TCP三次握手和四次挥手&#xff08;看下图&#xff09;三次握手常见问题&#xff1f;为什么是三次握手而不是两次&#xff1f;三次握手和哪些函数有关&#xff1f;TCP的生命周期是从什么时候开始的&#xff1f; 四次挥手通信状态…...

    2024/5/1 14:12:24
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/2 9:28:15
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/2 15:04:34
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57