1. 认证,授权,鉴权,权限控制

认证:你是谁
授权:你能做什么
鉴权:你能不能做(你可以用钱买房子,但是你的钱不够)
权限控制:同意/不同意做

2. Authorization,Authentication的记法

Authorization的or抽象成author,是人做的事,即授权,Authentication是认证

3. SpringSecurity和shiro

springsecurity

  • .功能全面
  • .专为web开发设计
  • .旧版本不能脱离web使用
  • .重量级框架
  • .是构建oauth的基础

shiro

  • 轻量级
  • 通用性好
  • 可脱离web使用

4. 核心组件Authentication


/**
一个这个接口的实现相当于包含用户密码的实体
**/
public interface Authentication extends Principal, Serializable {//获取用户的权限Collection<? extends GrantedAuthority> getAuthorities();//获取凭证,相当于获取密码Object getCredentials();//获取详细的用户信息,ip之类的Object getDetails();//获取用户信息,例如含有用户名的实体类Object getPrincipal();//是否已经认证了boolean isAuthenticated();//更改认证状态void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

5. GrantedAuthority


public interface GrantedAuthority extends Serializable {/**返回的是字符串,说明用字符串表示权限*/String getAuthority();
}

6. SecurityContextHolder

在这里插入图片描述

/**
先看initialize()这个方法,判断strategyName是否为空,为空则设置为本地线程模式(MODE_THREADLOCAL),
然后创建SecurityContextHolderStrategy对象,ThreadLocalSecurityContextHolderStrategy是SecurityContextHolderStrategy的实现类,而ThreadLocalSecurityContextHolderStrategy用ThreadLocal<SecurityContext>保存SecurityContext,SecurityContext类的属性有Authentication,所以SecurityContextHolder存着Authentication
*/public class SecurityContextHolder {public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";public static final String MODE_GLOBAL = "MODE_GLOBAL";public static final String SYSTEM_PROPERTY = "spring.security.strategy";//从系统属性中获取策略private static String strategyName = System.getProperty(SYSTEM_PROPERTY);private static SecurityContextHolderStrategy strategy;private static int initializeCount = 0;static {initialize();}private static void initialize() {//如果没有给定策略,设置默认策略if (!StringUtils.hasText(strategyName)) {// Set defaultstrategyName = MODE_THREADLOCAL;}//如果是本地策略,new一个ThreadLocalSecurityContextHolderStrategy对象if (strategyName.equals(MODE_THREADLOCAL)) {strategy = new ThreadLocalSecurityContextHolderStrategy();}else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {strategy = new InheritableThreadLocalSecurityContextHolderStrategy();}else if (strategyName.equals(MODE_GLOBAL)) {strategy = new GlobalSecurityContextHolderStrategy();}else {//尝试使用自定义策略// Try to load a custom strategytry {Class<?> clazz = Class.forName(strategyName);Constructor<?> customStrategy = clazz.getConstructor();strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();}catch (Exception ex) {ReflectionUtils.handleReflectionException(ex);}}initializeCount++;}
}
public interface SecurityContextHolderStrategy {void clearContext();SecurityContext getContext();void setContext(SecurityContext context);SecurityContext createEmptyContext();
}
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();public void clearContext() {contextHolder.remove();}public SecurityContext getContext() {SecurityContext ctx = contextHolder.get();if (ctx == null) {ctx = createEmptyContext();contextHolder.set(ctx);}return ctx;}public void setContext(SecurityContext context) {Assert.notNull(context, "Only non-null SecurityContext instances are permitted");contextHolder.set(context);}public SecurityContext createEmptyContext() {return new SecurityContextImpl();}
}
package org.springframework.security.core.context;
import org.springframework.security.core.Authentication;
import java.io.Serializable;
public interface SecurityContext extends Serializable {Authentication getAuthentication();void setAuthentication(Authentication authentication);
}

7. UserDetails和UserDetailsService

package org.springframework.security.core.userdetails;public interface UserDetailsService {UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
package org.springframework.security.core.userdetails;import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.io.Serializable;
import java.util.Collection;public interface UserDetails extends Serializable {Collection<? extends GrantedAuthority> getAuthorities();String getPassword();String getUsername();/*** Indicates whether the user's account has expired. An expired account cannot be* authenticated.** @return <code>true</code> if the user's account is valid (ie non-expired),* <code>false</code> if no longer valid (ie expired)*/boolean isAccountNonExpired();/*** Indicates whether the user is locked or unlocked. A locked user cannot be* authenticated.** @return <code>true</code> if the user is not locked, <code>false</code> otherwise*/boolean isAccountNonLocked();/*** Indicates whether the user's credentials (password) has expired. Expired* credentials prevent authentication.** @return <code>true</code> if the user's credentials are valid (ie non-expired),* <code>false</code> if no longer valid (ie expired)*/boolean isCredentialsNonExpired();/*** Indicates whether the user is enabled or disabled. A disabled user cannot be* authenticated.** @return <code>true</code> if the user is enabled, <code>false</code> otherwise*/boolean isEnabled();
}

8. AuthenticationManager

认证流程

public interface AuthenticationManager {Authentication authenticate(Authentication authentication)throws AuthenticationException;
}

9. ProviderManager

在这里插入图片描述

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {private static final Log logger = LogFactory.getLog(ProviderManager.class);private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();private List<AuthenticationProvider> providers = Collections.emptyList();protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();//认证管理可认证private AuthenticationManager parent;private boolean eraseCredentialsAfterAuthentication = true;/**这里使用了委托者模式AuthenticationProvider是一个接口,也有AuthenticationManager 接口的authenticate方法先遍历每个provider,用每个provider尝试认证,有一个成功即可;如果都失败了,则使用parent尝试认证,如果parent也认证失败,抛出异常*/public Authentication authenticate(Authentication authentication) throws AuthenticationException {Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;AuthenticationException parentException = null;Authentication result = null;Authentication parentResult = null;boolean debug = logger.isDebugEnabled();for (AuthenticationProvider provider : getProviders()) {//如果当前provider不支持认证这种类型,跳过if (!provider.supports(toTest)) {continue;}if (debug) {logger.debug("Authentication attempt using "+ provider.getClass().getName());}try {//使用当前认证提供者尝试认证result = provider.authenticate(authentication);//如果结果不为空则跳出循坏if (result != null) {copyDetails(authentication, result);break;}}catch (AccountStatusException | InternalAuthenticationServiceException e) {prepareException(e, authentication);throw e;} catch (AuthenticationException e) {lastException = e;}}//如果认证结果为空且parent不为空if (result == null && parent != null) {// Allow the parent to try.try {//让父亲去认证result = parentResult = parent.authenticate(authentication);}catch (ProviderNotFoundException e) {}catch (AuthenticationException e) {lastException = parentException = e;}}//if (result != null) {if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {((CredentialsContainer) result).eraseCredentials();}if (parentResult == null) {eventPublisher.publishAuthenticationSuccess(result);}return result;}if (lastException == null) {lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",new Object[] { toTest.getName() },"No AuthenticationProvider found for {0}"));}if (parentException == null) {prepareException(lastException, authentication);}throw lastException;}
}

10. 认证流程

在这里插入图片描述

思路
既然认证是通过AuthenticationProvider进行的,那么去找它的实现类;DaoAuthenticationProvider是AuthenticationProvider的实现类,AuthenticationProvider继承AbstractUserDetailsAuthenticationProvider抽象类,而AbstractUserDetailsAuthenticationProvider实现AuthenticationProvider,所以重点关注AbstractUserDetailsAuthenticationProvider这个抽象类

public abstract class AbstractUserDetailsAuthenticationProvider implementsAuthenticationProvider, InitializingBean, MessageSourceAware {protected final Log logger = LogFactory.getLog(getClass());protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();private UserCache userCache = new NullUserCache();private boolean forcePrincipalAsString = false;protected boolean hideUserNotFoundExceptions = true;private UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks();private UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();protected abstract void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication)throws AuthenticationException;public final void afterPropertiesSet() throws Exception {Assert.notNull(this.userCache, "A user cache must be set");Assert.notNull(this.messages, "A message source must be set");doAfterPropertiesSet();}//重点关注这个方法,因为AuthenticationProvider调用这个方法进行验证public Authentication authenticate(Authentication authentication) throws AuthenticationException {//判断authentication是否属于UsernamePasswordAuthenticationToken这种类型的Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,() -> messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports","Only UsernamePasswordAuthenticationToken is supported"));// Determine username//获取用户名String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED": authentication.getName();boolean cacheWasUsed = true;//从缓存获取用户信息UserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {/**检索用户信息实现此方法的类DaoAuthenticationProvider里的关键代码UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);**/user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);}catch (UsernameNotFoundException notFound) {logger.debug("User '" + username + "' not found");if (hideUserNotFoundExceptions) {throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials"));}else {throw notFound;}}Assert.notNull(user,"retrieveUser returned null - a violation of the interface contract");}try {/**前置认证检查账户是否上锁,是否可用,是否过期等*/preAuthenticationChecks.check(user);/**额外认证检查 判断密码是否匹配实现此方法的类DaoAuthenticationProvider里的关键代码if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {logger.debug("Authentication failed: password does not match stored value");throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials"));}*/additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);}catch (AuthenticationException exception) {if (cacheWasUsed) {// There was a problem, so try again after checking// we're using latest data (i.e. not from the cache)cacheWasUsed = false;user = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);preAuthenticationChecks.check(user);additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);}else {throw exception;}}/**后置检查检查凭证是否过期private class DefaultPostAuthenticationChecks implements UserDetailsChecker {public void check(UserDetails user) {if (!user.isCredentialsNonExpired()) {logger.debug("User account credentials have expired");throw new CredentialsExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.credentialsExpired","User credentials have expired"));}}}*/postAuthenticationChecks.check(user);if (!cacheWasUsed) {this.userCache.putUserInCache(user);}// 就是UserDetailsObject principalToReturn = user;if (forcePrincipalAsString) {principalToReturn = user.getUsername();}/**如果成功返回UsernamePasswordAuthenticationTokenprotected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(),authoritiesMapper.mapAuthorities(user.getAuthorities()));result.setDetails(authentication.getDetails());return result;}*/return createSuccessAuthentication(principalToReturn, authentication, user);}protected Authentication createSuccessAuthentication(Object principal,Authentication authentication, UserDetails user) {// Ensure we return the original credentials the user supplied,// so subsequent attempts are successful even with encoded passwords.// Also ensure we return the original getDetails(), so that future// authentication events after cache expiry contain the detailsUsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(),authoritiesMapper.mapAuthorities(user.getAuthorities()));result.setDetails(authentication.getDetails());return result;}protected void doAfterPropertiesSet() throws Exception {}public UserCache getUserCache() {return userCache;}public boolean isForcePrincipalAsString() {return forcePrincipalAsString;}public boolean isHideUserNotFoundExceptions() {return hideUserNotFoundExceptions;}protected abstract UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)throws AuthenticationException;public void setForcePrincipalAsString(boolean forcePrincipalAsString) {this.forcePrincipalAsString = forcePrincipalAsString;}public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;}public void setMessageSource(MessageSource messageSource) {this.messages = new MessageSourceAccessor(messageSource);}public void setUserCache(UserCache userCache) {this.userCache = userCache;}public boolean supports(Class<?> authentication) {return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));}private class DefaultPreAuthenticationChecks implements UserDetailsChecker {public void check(UserDetails user) {if (!user.isAccountNonLocked()) {logger.debug("User account is locked");throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked","User account is locked"));}if (!user.isEnabled()) {logger.debug("User account is disabled");throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled","User is disabled"));}if (!user.isAccountNonExpired()) {logger.debug("User account is expired");throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired","User account has expired"));}}}private class DefaultPostAuthenticationChecks implements UserDetailsChecker {public void check(UserDetails user) {if (!user.isCredentialsNonExpired()) {logger.debug("User account credentials have expired");throw new CredentialsExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.credentialsExpired","User credentials have expired"));}}}
}

11. Filter回顾

过程
请求到达Servlet,然后依次执行过滤器,若过滤器的拦截的url相同,则默认按名称排序执行, 以栈的形式先进后出执行

12. 核心过滤器之 SecurityContextPersistenceFilter

  作用: 创建SecurityContext和销毁SecurityContext

13. 核心过滤器之UsernamePasswordAuthenticationFilter

  作用:生成UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter继承AbstractAuthenticationProcessingFilter抽象类

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;private boolean postOnly = true;public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {//检查是否是post登录if (postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}//从request获取usernameString username = obtainUsername(request);//从request获取passwordString password = obtainPassword(request);if (username == null) {username = "";}if (password == null) {password = "";}username = username.trim();//生成tokenUsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);// Allow subclasses to set the "details" propertysetDetails(request, authRequest);/**开始认证,会交给AuthenticationProvider去做*/return this.getAuthenticationManager().authenticate(authRequest);}@Nullableprotected String obtainPassword(HttpServletRequest request) {return request.getParameter(passwordParameter);}@Nullableprotected String obtainUsername(HttpServletRequest request) {return request.getParameter(usernameParameter);}
}
  1. BasicAuthenticationFilter
public class BasicAuthenticationFilter extends OncePerRequestFilter {private AuthenticationEntryPoint authenticationEntryPoint;private AuthenticationManager authenticationManager;private RememberMeServices rememberMeServices = new NullRememberMeServices();private boolean ignoreFailure = false;private String credentialsCharset = "UTF-8";private BasicAuthenticationConverter authenticationConverter = new BasicAuthenticationConverter();@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {final boolean debug = this.logger.isDebugEnabled();try {/**BasicAuthenticationConverter的convert方法//    public static final String AUTHORIZATION = "Authorization";可以看到是从请求头中获取Authorization这个字段@Overridepublic UsernamePasswordAuthenticationToken convert(HttpServletRequest request) {//可以看到是从请求头中获取Authorization这个字段String header = request.getHeader(AUTHORIZATION);if (header == null) {return null;}header = header.trim();if (!StringUtils.startsWithIgnoreCase(header, AUTHENTICATION_SCHEME_BASIC)) {return null;}byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);byte[] decoded;try {//base64解码decoded = Base64.getDecoder().decode(base64Token);}catch (IllegalArgumentException e) {throw new BadCredentialsException("Failed to decode basic authentication token");}String token = new String(decoded, getCredentialsCharset(request));//以冒号分割所以token内容应该是username:password这种形式int delim = token.indexOf(":");if (delim == -1) {throw new BadCredentialsException("Invalid basic authentication token");}UsernamePasswordAuthenticationToken result  = new UsernamePasswordAuthenticationToken(token.substring(0, delim), token.substring(delim + 1));result.setDetails(this.authenticationDetailsSource.buildDetails(request));return result;}*/UsernamePasswordAuthenticationToken authRequest = authenticationConverter.convert(request);if (authRequest == null) {chain.doFilter(request, response);return;}String username = authRequest.getName();if (authenticationIsRequired(username)) {//开始认证Authentication authResult = this.authenticationManager.authenticate(authRequest);if (debug) {this.logger.debug("Authentication success: " + authResult);}SecurityContextHolder.getContext().setAuthentication(authResult);this.rememberMeServices.loginSuccess(request, response, authResult);onSuccessfulAuthentication(request, response, authResult);}}catch (AuthenticationException failed) {SecurityContextHolder.clearContext();if (debug) {this.logger.debug("Authentication request for failed: " + failed);}this.rememberMeServices.loginFail(request, response);onUnsuccessfulAuthentication(request, response, failed);if (this.ignoreFailure) {chain.doFilter(request, response);}else {this.authenticationEntryPoint.commence(request, response, failed);}return;}chain.doFilter(request, response);}}

15. AnonymousAuthenticationFilter

public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();private String key;private Object principal;private List<GrantedAuthority> authorities;public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {if (SecurityContextHolder.getContext().getAuthentication() == null) {//获取不到认证信息,直接创建新的认证信息SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));if (logger.isDebugEnabled()) {logger.debug("Populated SecurityContextHolder with anonymous token: '"+ SecurityContextHolder.getContext().getAuthentication() + "'");}}else {if (logger.isDebugEnabled()) {logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"+ SecurityContextHolder.getContext().getAuthentication() + "'");}}chain.doFilter(req, res);}protected Authentication createAuthentication(HttpServletRequest request) {//匿名用户用AnonymousAuthenticationToken替代UsernamePasswordAuthenticationToken AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,principal, authorities);auth.setDetails(authenticationDetailsSource.buildDetails(request));return auth;}
}

16. 完整流程

请添加图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

17. 权限认证

  • WebAsyncManagerIntegrationFilter
    在 Callable 上填充 SecurityContext,提供 SecurityContext 和 Spring Web 的 WebAsyncManager 之 间的集成。
  • (重要)SecurityContextPersistenceFilter
    在请求之前使用从配置的 SecurityContextRepository 获取的信息填充 SecurityContextHolder,并在请求完成并清除上下文持有者后将其存储回存储库
  • org.springframework.security.web.header.HeaderWriterFilter
    过滤器实现以向当前响应添加标头。 添加某些启用浏览器保护的标头可能很有用。 像 X-Frame-Options、X-XSS-Protection 和 X-Content-Type-Options。
  • (重要)UsernamePasswordAuthenticationFilter
    处理身份验证表单提交。 在 Spring Security 3.0 之前调用 AuthenticationProcessingFilter。
    登录表单必须向此过滤器提供两个参数:用户名和密码。 要使用的默认参数名称包含在静态字段 SPRING_SECURITY_FORM_USERNAME_KEY 和 SPRING_SECURITY_FORM_PASSWORD_KEY 中。 也可以通过设置 usernameParameter 和 passwordParameter 属性来更改参数名称。
    默认情况下,此过滤器响应 URL /login。
  • DefaultLoginPageGeneratingFilter
    在用户未配置登录页面的情况下,用于名称空间配置的内部使用。 配置代码将在链中插入这个过滤器。 仅当重定向用于登录页面时才有效。
  • DefaultLogoutPageGeneratingFilter
    生成默认的注销页面。
  • RequestCacheAwareFilter
    如果缓存的请求与当前请求匹配,则负责重新构造已保存的请求
  • SecurityContextHolderAwareRequestFilter
    一个过滤器,它用实现servlet API安全方法的请求包装器填充ServletRequest。
  • AnonymousAuthenticationFilter
    检测SecurityContextHolder中是否没有Authentication对象,并在需要时使用一个对象填充它
  • SessionManagementFilter
    检测用户从请求开始时就已经通过了身份验证,如果已经通过了身份验证,则调用配置的SessionAuthenticationStrategy来执行任何与会话相关的活动,例如激活会话固定保护机制或检查多个并发登录。
  • ExceptionTranslationFilter
    处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException
  • (重要)FilterSecurityInterceptor
    通过过滤器实现执行HTTP资源的安全处理。

关键过滤器FilterSecurityInterceptor

在这里插入图片描述
思路:FilterSecurityInterceptor是filter,就一定有dofilter这个方法,所以先找dofilter

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";private FilterInvocationSecurityMetadataSource securityMetadataSource;private boolean observeOncePerRequest = true;public void init(FilterConfig arg0) {}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {//这里是把request和response做了类型转换/**FilterInvocation构造函数public FilterInvocation(ServletRequest request, ServletResponse response,FilterChain chain) {if ((request == null) || (response == null) || (chain == null)) {throw new IllegalArgumentException("Cannot pass null values to constructor");}this.request = (HttpServletRequest) request;this.response = (HttpServletResponse) response;this.chain = chain;}*/FilterInvocation fi = new FilterInvocation(request, response, chain);invoke(fi);}public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {return this.securityMetadataSource;}public SecurityMetadataSource obtainSecurityMetadataSource() {return this.securityMetadataSource;}public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {this.securityMetadataSource = newSource;}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 checkingfi.getChain().doFilter(fi.getRequest(), fi.getResponse());}else {//第一次处理// first time this request being called, so perform security checkingif (fi.getRequest() != null && observeOncePerRequest) {//相当于做标记,有这个标记说明处理过了fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);}InterceptorStatusToken token = super.beforeInvocation(fi);try {fi.getChain().doFilter(fi.getRequest(), fi.getResponse());}finally {super.finallyInvocation(token);}super.afterInvocation(token, null);}}
}
public abstract class AbstractSecurityInterceptor implements InitializingBean,ApplicationEventPublisherAware, MessageSourceAware {//访问决策管理器private AccessDecisionManager accessDecisionManager;private AfterInvocationManager afterInvocationManager;private RunAsManager runAsManager = new NullRunAsManager();private boolean alwaysReauthenticate = false;private boolean rejectPublicInvocations = false;private boolean validateConfigAttributes = true;private boolean publishAuthorizationSuccess = false;protected InterceptorStatusToken beforeInvocation(Object object) {//这里的object是FilterInvocation的对象Assert.notNull(object, "Object was null");//从securityMetadataSource获取权限列表,securityMetadataSource在构造FilterSecurityInterceptor时传入Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);if (attributes == null || attributes.isEmpty()) {//最好把rejectPublicInvocations设置为true,因为该用户没有权限,不应该让用户访问下去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'");}publishEvent(new PublicInvocationEvent(object));return null; // no further work post-invocation}//获取登录用户的认证信息,不能为空if (SecurityContextHolder.getContext().getAuthentication() == null) {credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound","An Authentication object was not found in the SecurityContext"),object, attributes);}//是否需要认证,是则去认证Authentication authenticated = authenticateIfRequired();// Attempt authorizationtry {//这里是重点,调用访问决策管理器去决定让该用户访问该url(权限)this.accessDecisionManager.decide(authenticated, object, attributes);}catch (AccessDeniedException accessDeniedException) {publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,accessDeniedException));throw accessDeniedException;}//不抛AccessDeniedException就是允许访问if (debug) {logger.debug("Authorization successful");}}protected void finallyInvocation(InterceptorStatusToken token) {if (token != null && token.isContextHolderRefreshRequired()) {if (logger.isDebugEnabled()) {logger.debug("Reverting to original Authentication: "+ token.getSecurityContext().getAuthentication());}SecurityContextHolder.setContext(token.getSecurityContext());}}//这个方法与beforeInvocation同理,区别在于beforeInvocation在进入controller方法之前做决策,afterInvocation在controller方法返回之后做决策,根据filterchain的效果推出的protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {if (token == null) {// public objectreturn returnedObject;}finallyInvocation(token); // continue to clean in this method for passivityif (afterInvocationManager != null) {// Attempt after invocation handlingtry {returnedObject = afterInvocationManager.decide(token.getSecurityContext().getAuthentication(), token.getSecureObject(), token.getAttributes(), returnedObject);}catch (AccessDeniedException accessDeniedException) {AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token.getAttributes(), token.getSecurityContext().getAuthentication(),accessDeniedException);publishEvent(event);throw accessDeniedException;}}return returnedObject;}
}
/**
AccessDecisionManager有4个实现类,其中1个是抽象实现,另外3个分别是在抽象实现上再具体实现
抽象实现类是AbstractAccessDecisionManager;AffirmativeBased(一票同意),ConsensusBased(少数服从多数)和UnanimousBased(全部同意)继承AbstractAccessDecisionManager
这里我们只看AffirmativeBased
*/
public interface AccessDecisionManager {void decide(Authentication authentication, Object object,Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,InsufficientAuthenticationException;boolean supports(ConfigAttribute attribute);boolean supports(Class<?> clazz);
}
public class AffirmativeBased extends AbstractAccessDecisionManager {//需要决策器列表public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {super(decisionVoters);}public void decide(Authentication authentication, Object object,Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {//拒绝的票数int deny = 0;//遍历所有访问决策器for (AccessDecisionVoter voter : getDecisionVoters()) {//决策结果int result = voter.vote(authentication, object, configAttributes);switch (result) {case AccessDecisionVoter.ACCESS_GRANTED://同意票return;case AccessDecisionVoter.ACCESS_DENIED://拒绝票deny++;break;default:break;}}//拒绝票大于0拒绝该用户访问if (deny > 0) {throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));}}
}
//访问决策器接口,这里只看实现类RoleVoter
public interface AccessDecisionVoter<S> {//同意int ACCESS_GRANTED = 1;//弃权int ACCESS_ABSTAIN = 0;//拒绝int ACCESS_DENIED = -1;boolean supports(ConfigAttribute attribute);boolean supports(Class<?> clazz);int vote(Authentication authentication, S object,Collection<ConfigAttribute> attributes);
}
public class RoleVoter implements AccessDecisionVoter<Object> {private String rolePrefix = "ROLE_";public String getRolePrefix() {return rolePrefix;}//可以设置角色前缀public void setRolePrefix(String rolePrefix) {this.rolePrefix = rolePrefix;}//这就是为什么角色要以ROLE_开头public boolean supports(ConfigAttribute attribute) {if ((attribute.getAttribute() != null)&& attribute.getAttribute().startsWith(getRolePrefix())) {return true;}else {return false;}}public boolean supports(Class<?> clazz) {return true;}//关键代码public int vote(Authentication authentication, Object object,Collection<ConfigAttribute> attributes) {//如果认证信息为空,拒绝if (authentication == null) {return ACCESS_DENIED;}//先赋值弃权int result = ACCESS_ABSTAIN;//Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);//这里的attribute是角色 ,遍历当前用户的角色for (ConfigAttribute attribute : attributes) {if (this.supports(attribute)) {//如果支持当前类型,把弃权改为拒绝	result = ACCESS_DENIED;// Attempt to find a matching granted authority//遍历访问url需要的角色for (GrantedAuthority authority : authorities) {//判断角色是否相等if (attribute.getAttribute().equals(authority.getAuthority())) {return ACCESS_GRANTED;}}}}return result;}Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {return authentication.getAuthorities();}
}

18. 权限过滤的第二种方式

通过查看源码的方式可知,权限过滤有两种方法。一种是上面这种基于filter的,另一种是注解方式
这里说注解方式
看GlobalMethodSecurityConfiguration注释可知怎么自定义

思路
我们知道通过@PreAuthorize配置访问方法所需的权限,而启用该注解的方式是使用@EnableGlobalMethodSecurity。从@EnableGlobalMethodSecurity的文档说明可以找到配置类GlobalMethodSecurityConfiguration 。该配置类有一个bean返回的是MethodSecurityInterceptor的对象,
MethodSecurityInterceptor继承AbstractSecurityInterceptor,通过前面我们知道,AbstractSecurityInterceptor是FilterSecurityInterceptor的父类,FilterSecurityInterceptor是基于过滤器的而MethodSecurityInterceptor是基于注解的。MethodSecurityInterceptor有一个invoke方法,这个方法调用了AbstractSecurityInterceptor的beforeInvocation方法,beforeInvocation会先通过数据源获取attributes,然后用attributes作为访问决策管理器的decide的参数去做决策,没有抛AccessDeniedException异常就算通过,这就是权限过滤的大概流程

数据源(SecurityMetadataSource)

通过debug可知,MethodSecurityInterceptor使用数据源PrePostAnnotationSecurityMetadataSource,
PrePostAnnotationSecurityMetadataSource继承自AbstractMethodSecurityMetadataSource

访问决策管理器(AccessDecisionManager)

通过debug可知,MethodSecurityInterceptor使用AffirmativeBased(一票通过)

访问决策器(AccessDecisionVoter)

通过debug可知,MethodSecurityInterceptor的访问决策器有PreInvocationAuthorizationAdviceVoter,
PreInvocationAuthorizationAdviceVoter通过自身的属性PreInvocationAuthorizationAdvice的before方法活的投票结果

19. 自定义权限过滤

思路:一切都从AbstractSecurityInterceptor抽象类开始,若基于过滤器就参考FilterSecurityInterceptor,基于注解就参考MethodSecurityInterceptor,二选一即可。无论选哪种,有3个必要的组件,数据源、访问决策管理器和访问决策器

技巧

  • 基于过滤器不想重写FilterSecurityInterceptor可以这样做
    @Overrideprotected void configure(HttpSecurity http) throws Exception {.setAccessDecisionManager(new AffirmativeBased(Arrays.asList(new AnnotationPermissionVoter())));//这个不是基于session一定要关了http.csrf().disable();http.authorizeRequests().antMatchers("/login").permitAll().antMatchers("/logout").permitAll()//需要认证.anyRequest().authenticated();//开启表单登录http.formLogin();http.authorizeRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {@Overridepublic <O extends FilterSecurityInterceptor> O postProcess(O o) {//配置数据源o.setSecurityMetadataSource(new MyFilterSecurityMetadataSource());//配置访问决策管理器o.setAccessDecisionManager(getAffirmativeBased());return o;}});}
  • 基于注解可以这样写
    @ResourceMethodSecurityInterceptor methodSecurityInterceptor;@Overrideprotected void configure(HttpSecurity http) throws Exception {//配置数据源methodSecurityInterceptor.setAccessDecisionManager(getAccessDecisionManager());//配置访问决策管理器methodSecurityInterceptor.setSecurityMetadataSource(getSecurityMetadataSource());//这个不是基于session一定要关了http.csrf().disable();http.authorizeRequests().antMatchers("/login").permitAll().antMatchers("/logout").permitAll()//需要认证.anyRequest().authenticated();//开启表单登录http.formLogin();}

20. 自定义认证

思路:写过滤器解析放在请求头的token,禁用session,

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. for example: not eligible for auto-proxying

    当在本地启动服务时&#xff0c;报如下错误 2021-11-07 12:53:35.962 [restartedMain] INFO o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization - Bean org.springframework.cloud.autoconfigure.ConfigurationPropertie…...

    2024/5/6 11:20:54
  2. Java中的按位操作符

    最近看 JDK 的源码, 看到一些 按位运算(Bitwise Operators), 这里做个总结. 1 按位运算符汇总 OperatorDescription&Bitwise AND(按位与)|Bitwise OR(按位或)^Bitwise XOR(eXclusive OR, 按位异或)~Bitwise Complement(按位取反)<<Left Shift(左移)>>Signed …...

    2024/4/18 4:37:15
  3. 设计模式——(13)模板模式

    1、定义 定义一个模板结构&#xff0c;将具体内容延迟到子类去实现。 2、主要作用 在不改变模板结构的前提下在子类中重新定义模板中的内容。 3、解决的问题 提高代码复用性 将相同部分的代码放在抽象的父类中&#xff0c;而将不同的代码放入不同的子类中 实现了反向控制 …...

    2024/4/20 4:46:25
  4. 设计模式——(14)命令模式Command

    1、定义 将一个请求封装为一个对象&#xff0c;使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通&#xff0c;这样方便将命令对象进行储存、传递、调用、增加与管理。 2、UML类图 命令角色&#xff08;Command&#xff09;&#xff1a;定义命令的接…...

    2024/4/7 3:20:59
  5. MFC--界面输入

    编辑界面 菜单 对话框 注意:单选按钮要添加组属性 添加变量 CMydlg变量 CTestView 添加消息 OnShow 编辑消息 CTestView OnDraw void CTestView::OnDraw(CDC* pDC) {CTestDoc* pDoc GetDocument();ASSERT_VALID(pDoc);// TODO: add draw code for native data herefor(…...

    2024/4/19 12:00:48
  6. 3D-LaneNet+: Anchor Free Lane Detection using a Semi-Local Representation

    动机&#xff1a; 对3D-LaneNet的改进&#xff1b; 特点 semi-local tile representation&#xff1a; breaks down lanes into simple lane segments whose parameters can be learnt 【CC】网格化&#xff0c;基于每个网格去学习Lane的特征&#xff1b;最后再通过NN合起来&a…...

    2024/4/25 21:03:28
  7. 设计模式——(11)享元模式

    1、定义 运用共享技术有效地支持大量细粒度对象的复用。 使用享元模式前&#xff1a; 使用享元模式后&#xff1a; 2、单纯享元模式 &#xff08;1&#xff09;、UML类图 抽象享元Flyweight类&#xff1a;它简单地说就是一个产品的抽象类&#xff0c;同时定义出对象的外部状…...

    2024/4/14 20:24:10
  8. python 要配置3.10环境

    在anaconda中配置enviroment即可 参考链接1 参考链接2...

    2024/4/14 20:24:00
  9. 设计模式——(12)代理模式

    1、定义 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时&#xff0c;访问对象不适合或者不能直接引用目标对象&#xff0c;代理对象作为访问对象和目标对象之间的中介。 2、UML类图 抽象主题角色&#xff08;Subject&#xff09;&#xff1a;声明了目标对象…...

    2024/5/6 13:09:16
  10. 如何批量给视频生成周边渐晕效果

    如何批量给视频生成周边渐晕效果&#xff0c;首先我们在手机上安装应用程序“王者剪辑app”&#xff0c;启动软件并进入一键剪辑中的“特效应用”功能&#xff0c; 批量导入或导入一个视频&#xff0c; 接着选择“周边渐晕”效果类型&#xff0c;然后点击界面右上角的对勾按…...

    2024/4/17 22:56:54
  11. 每天5个python小技巧(day6)

    面向对象篇 ​ “如果你读自己6个月前写的代码并且感觉仍然很好&#xff0c;那么可能就有些不对劲了。这通常意味着你这6个月没有学到新东西。” ----Brett Cannon 文章目录面向对象篇1.classmethod -- 类方法2.staticmethod -- 静态方法3.实例方法4.property -- 内置描述符5.…...

    2024/5/6 5:31:59
  12. python机器学习 多项式回归模型正则化(拉索,岭,弹性网络)

    多项式回归模型正则化&#xff08;拉索&#xff0c;岭&#xff0c;弹性网&#xff09; 目录多项式回归模型正则化&#xff08;拉索&#xff0c;岭&#xff0c;弹性网&#xff09;一、多项式回归模型正则化&#xff1a;1.L1正则化&#xff08;lasso)回归2.L2正则化&#xff08;R…...

    2024/4/16 18:48:30
  13. leetcode刷题——链表【2】

    206. 反转链表 指针法&#xff1a; cur&#xff1a;当前用来遍历的指针 pre&#xff1a;要赋值的指针 temp&#xff1a;保存原cur指向的位置 class Solution:def reverseList(self, head: ListNode) -> ListNode:curheadpreNonewhile cur!None:tempcur.next # 下一个遍历的…...

    2024/4/18 3:46:14
  14. Sentinel之热点Key限流

    前面一章已经对Sentinel做出了介绍&#xff0c;并且也讲述了Sentinel的降级、流控规则&#xff0c;那么现在来对Sentinel热点Key限流做个知识点总结&#xff01;&#xff01;&#xff01; 介绍&#xff1a; 上一章遇到的问题思考&#xff1a; 编写代码&#xff08;自定义兜底方…...

    2024/4/14 20:25:16
  15. p-NCS-Bz-DFO,CAS1222468-90-7纯度≥95%仅供实验室使用

    名称&#xff1a;p-SCN-Bn-Deferoxamine p-NCS-Bz-DFO 分子式&#xff1a;C₃₃H₅₂N₈O₈S₂ 化学名称&#xff1a;1-(4-isothiocyanatophenyl)-3-[6,17-dihydroxy-7,10,18,21-tetraoxo-27-(N-acetylhydroxylamino)- 6,11,17, 22- tetraazaheptaeicosine] thiourea 分子量…...

    2024/5/6 15:43:47
  16. git仓库的创建以及连接远程仓库

    &#xff08;供自己后期复习使用,如果哪里不对希望大佬指正&#xff09; 1.先创建远程仓库 2.本地仓库创建以及连接远程仓库 1.配置个人信息 git config --global user.name "自己git的name" git config --global user.email "自己git用的邮箱"2.在本地…...

    2024/5/6 16:07:59
  17. 试题 基础练习 十六进制转八进制_python

    资源限制 时间限制&#xff1a;1.0s 内存限制&#xff1a;512.0MB 问题描述  给定n个十六进制正整数&#xff0c;输出它们对应的八进制数。输入格式  输入的第一行为一个正整数n &#xff08;1<n<10&#xff09;。  接下来n行&#xff0c;每行一个由0~9、大写字…...

    2024/4/14 20:25:01
  18. 5918. 统计字符串中的元音子字符串

    题目链接&#xff1a;力扣 思路&#xff1a;暴力三层循环&#xff0c;判断所有子字符串是否符合题意即可。注意&#xff1a;需要子字符串5个元音字符都要有。我是用map来标记的~~~map的大小就是子字符串有几个元音字符~~ 上代码&#xff1a; class Solution {fun countVowel…...

    2024/4/14 20:24:51
  19. qq 微信 无法访问个人文件夹

    昨天某个时刻开始&#xff0c;QQ会出现所有图片无法加载的状况&#xff0c;微信会出现截图发到聊天框是文件的问题。 一开始不以为然&#xff0c;直到第二天重启电脑发现两个软件都登陆不上去了。对应显示的是标题的关键字&#xff0c;因为问题已经解决就不截图了。 解决方案&…...

    2024/5/5 21:00:14
  20. python实现管理chrome浏览器的书签管理(程序员の怒火之每次电脑重启打开chrome浏览器都收藏页都有京东、天猫等收藏页)

    python实现管理chrome浏览器的书签管理&#xff08;程序员の怒火之每次电脑重启打开chrome浏览器都收藏页都有京东、天猫等收藏页&#xff09; 问题简述&#xff1a;每次电脑重启打开chrome浏览器都收藏页都有京东、天猫等收藏页&#xff0c;删都删不干净&#xff0c;看着心烦…...

    2024/5/5 5:35:03

最新文章

  1. 微信小程序如何使用svg矢量图标

    微信小程序如何使用自定义SVG矢量图标 在微信小程序中&#xff0c;经常会用到小图标来装饰界面&#xff0c;我们常用的方法就是引用第三方的图标&#xff0c;但会存在收费或者找不到合适的图标&#xff0c;这时候我建议可以自行编写svg图标代码&#xff0c;就可以随心所欲的使…...

    2024/5/6 16:51:13
  2. 梯度消失和梯度爆炸的一些处理方法

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

    2024/5/6 9:38:23
  3. 【JavaScript】如何在npm中切换源以及使用指定源安装依赖

    忘不掉的是什么我也不知道 想不起当年模样 看也看不到 去也去不了的地方 也许那老街的腔调是属于我的忧伤 嘴角那点微笑越来越勉强 忘不掉的是什么我也不知道 放不下熟悉片段 回头望一眼 已经很多年的时间 透过手指间看着天 我又回到那老街 靠在你们身边渐行渐远 …...

    2024/5/5 8:47:29
  4. WIFI驱动移植实验:WIFI从路由器动态获取IP地址与联网

    一. 简介 前面两篇文章&#xff0c;一篇文章实现了WIFI联网前要做的工作&#xff0c;另一篇文章配置了WIFI配置文件&#xff0c;进行了WIFI热点的连接。文章如下&#xff1a; WIFI驱动移植实验&#xff1a;WIFI 联网前的工作-CSDN博客 WIFI驱动移植实验&#xff1a;连接WIF…...

    2024/5/4 17:27:10
  5. 【外汇早评】美通胀数据走低,美元调整

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

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

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

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

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

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

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

    2024/5/6 9:21:00
  9. 【外汇早评】日本央行会议纪要不改日元强势

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

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

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

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

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

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

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

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

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

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

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

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

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

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

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

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

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

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

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

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

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

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

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

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

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

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

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

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

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

    2024/5/4 23:55:01
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

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

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

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

    2022/11/19 21:17:18
  26. 错误使用 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
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

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

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

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

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

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

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

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

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

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

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

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

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,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
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2022/11/19 21:16:58
  44. 如何在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