前言:前段时间在搭建公司框架安全验证的时候,就想到之前web最火的shiro框架,所以就在空闲时间学习并总结一些搭建流程和解析,这里给大家出个简单的教程说明吧。

shiro的基本介绍这里就不再说了,可以自行百度相关的shiro教程,对于第一次接触shiro的小伙伴,博主推荐你可以先阅读该篇文章Shiro,对shiro进行初步的了解,以及它的核心原理。

一、什么是shiro?

shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。使用shiro实现系统 的权限管理,有效提高开发效率,从而降低开发成本。

1.1 shiro内部结构框架  官网架构说明

å¨è¿éæå¥å¾çæè¿°

1.2 外部看shiro架构 

api说明
Subject主体,代表当前‘用户’ 。这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委派给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
SecurityManager安全管理器;即所有与安全有关的操作都会与SecurityManager交互且它管理者所有Subject;可以看出它是Shiro的核心,它负责与后面介绍的其它组件进行交互以把它看成DispathcherServlet前端控制器
Realm域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

 1.3 shiro认证流程

 二、添加shiro相关依赖

<!--验证码 用于登录安全校验-->
<dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency><!--Shiro核心框架 -->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version>
</dependency><!-- Shiro使用Srping框架 --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version>
</dependency><!-- Shiro使用EhCache缓存框架 -->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version>
</dependency><!-- thymeleaf模板引擎和shiro框架的整合 -->
<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>${thymeleaf.extras.shiro.version}</version>
</dependency>

三、添加相关配置类

在这里,博主先使用ehcache对用户登录信息做缓存管理(推荐使用reids做缓存好些,可解决分布式架构下session共享问题,可参考分布式架构中shiro的实现)

ehcache-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="layduo" updateCheck="false"><!-- 磁盘缓存位置 --><diskStore path="java.io.tmpdir"/><!-- maxEntriesLocalHeap:堆内存中最大缓存对象数,0没有限制 --><!-- maxElementsInMemory: 在内存中缓存的element的最大数目。--><!-- eternal:elements是否永久有效,如果为true,timeouts将被忽略,element将永不过期 --><!-- timeToIdleSeconds:失效前的空闲秒数,当eternal为false时,这个属性才有效,0为不限制 --><!-- timeToLiveSeconds:失效前的存活秒数,创建时间到失效时间的间隔为存活时间,当eternal为false时,这个属性才有效,0为不限制 --><!-- overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上 --><!-- statistics:是否收集统计信息。如果需要监控缓存使用情况,应该打开这个选项。默认为关闭(统计会影响性能)。设置statistics="true"开启统计 --><!-- 默认缓存 --><defaultCachemaxEntriesLocalHeap="1000"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="3600"overflowToDisk="false"></defaultCache><!-- 登录记录缓存 锁定10分钟 --><cache name="loginRecordCache"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache><!-- 系统活跃用户缓存 --><cache name="sys-userCache"maxEntriesLocalHeap="10000"overflowToDisk="false"eternal="false"diskPersistent="false"timeToLiveSeconds="0"timeToIdleSeconds="0"statistics="true"></cache><!-- 系统会话缓存 --><cache name="shiro-activeSessionCache"maxElementsInMemory="10000"overflowToDisk="true"eternal="true"timeToLiveSeconds="0"timeToIdleSeconds="0"diskPersistent="true"diskExpiryThreadIntervalSeconds="600"></cache></ehcache>

application.yml中shiro相关配置参数

#shiro
shiro:user:#登录地址loginUrl: /login#权限认证失败地址unauthorizedUrl: /unauth#首页地址indexUrl: /index#验证码开关captchaEnabled: true#验证码类型: math数据计算、char字符检验captchaType: mathcookie:#设置cookie的域名 默认为空,即当前访问的域名domain:#设置cookie的有效访问路径path: /#设置HttpOnly属性httpOnly: true#设置cookie的过期时间,单位为天maxAge: 30session:#session超时时间,-1代表永不过期(默认为30分钟)expireTime: 30#同步session到数据库的周期(默认1分钟)dbSyncPeriod: 1#相隔多久检查一次session的有效性,默认10分钟validationInterval: 10#同一个用户最大会话数,比如1的意思是同一个账号允许最多同时一个人登录(默认-1不限制)maxSession: 1#踢出之前登录的/之后登录的用户,默认踢出之前登陆的用户kickoutAfter: false

shiro配置类 -- ShiroConfig.class

package com.layduo.framework.config;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;import javax.servlet.Filter;import org.apache.commons.io.IOUtils;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import com.layduo.common.utils.StringUtils;
import com.layduo.framework.shiro.realm.UserRealm;
import com.layduo.framework.shiro.web.filter.LogoutFilter;
import com.layduo.framework.shiro.web.filter.captcha.CaptchaValidateFilter;import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;/*** shiro相关配置* * @author layduo* @createTime 2019年11月7日 下午6:26:22*/
@Configuration
public class ShiroConfig {public static final String PREMISSION_STRING = "perms[\"{0}\"]";// Session超时时间,单位为毫秒(默认30分钟)@Value("${shiro.session.expireTime}")private int expireTime;// 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟@Value("${shiro.session.validationInterval}")private int validationInterval;// 同一个用户最大会话数@Value("${shiro.session.maxSession}")private int maxSession;// 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户@Value("${shiro.session.kickoutAfter}")private boolean kickoutAfter;// 验证码开关@Value("${shiro.user.captchaEnabled}")private boolean captchaEnabled;// 验证码类型@Value("${shiro.user.captchaType}")private String captchaType;// 设置Cookie的域名@Value("${shiro.cookie.domain}")private String domain;// 设置cookie的有效访问路径@Value("${shiro.cookie.path}")private String path;// 设置HttpOnly属性@Value("${shiro.cookie.httpOnly}")private boolean httpOnly;// 设置Cookie的过期时间,秒为单位@Value("${shiro.cookie.maxAge}")private int maxAge;// 登录地址@Value("${shiro.user.loginUrl}")private String loginUrl;// 权限认证失败地址@Value("${shiro.user.unauthorizedUrl}")private String unauthorizedUrl;@Beanpublic EhCacheManager getEhCacheManager() {// 获取ehcache-shiro.xml里对应的缓存管理器net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("layduo");EhCacheManager ehCacheManager = new EhCacheManager();if (StringUtils.isNull(cacheManager)) {ehCacheManager.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()));} else {ehCacheManager.setCacheManager(cacheManager);}return ehCacheManager;}/*** 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署* * @return*/protected InputStream getCacheManagerConfigFileInputStream() {String configFile = "classpath:ehcache/ehcache-shiro.xml";InputStream inputStream = null;try {inputStream = ResourceUtils.getInputStreamForPath(configFile);byte[] b = IOUtils.toByteArray(inputStream);InputStream in = new ByteArrayInputStream(b);return in;} catch (IOException e) {// 无法获取cacheManagerConfigFile的输入流throw new ConfigurationException("Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e);} finally {IOUtils.closeQuietly(inputStream);}}/*** 自定义Realm 认证授权逻辑实现* @param cacheManager* @return*/@Beanpublic UserRealm userRealm(EhCacheManager cacheManager) {UserRealm userRealm = new UserRealm();userRealm.setCacheManager(cacheManager);return userRealm;}/*** 安全管理器* @param userRealm* @return*/@Beanpublic SecurityManager securityManager(UserRealm userRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//设置realmsecurityManager.setRealm(userRealm);//注入缓存管理器securityManager.setCacheManager(getEhCacheManager());return securityManager;}/*** Shiro过滤器配置 、配置权限拦截规则指定跳转页面* @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();//Shiro的核心安全接口,这个属性是必须的shiroFilterFactoryBean.setSecurityManager(securityManager);//身份认证失败,则跳转到登录页面的配置shiroFilterFactoryBean.setLoginUrl(loginUrl);//权限认证失败,则跳转到指定页面shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);//Shiro连接约束配置,即过滤链的定义LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();//对静态资源设置匿名访问filterChainDefinitionMap.put("/favicon.ico**", "anon");filterChainDefinitionMap.put("/ruoyi.png**", "anon");filterChainDefinitionMap.put("/css/**", "anon");filterChainDefinitionMap.put("/docs/**", "anon");filterChainDefinitionMap.put("/fonts/**", "anon");filterChainDefinitionMap.put("/img/**", "anon");filterChainDefinitionMap.put("/ajax/**", "anon");filterChainDefinitionMap.put("/js/**", "anon");filterChainDefinitionMap.put("/ruoyi/**", "anon");filterChainDefinitionMap.put("/druid/**", "anon");filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");//退出logout地址,shiro清除sessionfilterChainDefinitionMap.put("/logout", "logout");//不需要拦截的访问filterChainDefinitionMap.put("/login", "anon,captchaValidate");Map<String, Filter> filters = new LinkedHashMap<String, Filter>();filters.put("captchaValidate", captchaValidateFilter());//注销成功,则跳转到指定页面filters.put("logout", logoutFilter());shiroFilterFactoryBean.setFilters(filters);//所有请求需要认证filterChainDefinitionMap.put("/**", "user");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}/*** 退出过滤器* @return*/public LogoutFilter logoutFilter() {LogoutFilter logoutFilter = new LogoutFilter();logoutFilter.setCacheManager(getEhCacheManager());logoutFilter.setLoginUrl(loginUrl);return logoutFilter;}/*** 自定义验证码过滤器* @return*/@Beanpublic CaptchaValidateFilter captchaValidateFilter() {CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();captchaValidateFilter.setCaptchaEnabled(captchaEnabled);captchaValidateFilter.setCaptchaType(captchaType);return captchaValidateFilter;}/*** cookie 属性设置 配置文件默认30天* @return*/public SimpleCookie rememberMeCookie() {SimpleCookie cookie = new SimpleCookie("rememberMe");cookie.setDomain(domain);cookie.setPath(path);cookie.setHttpOnly(httpOnly);cookie.setMaxAge(maxAge * 24 * 60 * 60);return cookie;}/*** 记住我* @return*/public CookieRememberMeManager rememberMeManager() {CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();cookieRememberMeManager.setCookie(rememberMeCookie());cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ=="));return cookieRememberMeManager;}/*** thymeleaf模板引擎和shiro框架整合* @return*/@Beanpublic ShiroDialect shiroDialect() {return new ShiroDialect();}/*** 开启Shiro注解通知器* @param securityManager* @return*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}
}

自定义UserRealm认证授权逻辑实现

package com.layduo.framework.shiro.realm;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;import com.layduo.common.exception.user.CaptchaException;
import com.layduo.common.exception.user.RoleBlockedException;
import com.layduo.common.exception.user.UserBlockedException;
import com.layduo.common.exception.user.UserNotExistsException;
import com.layduo.common.exception.user.UserPasswordNotMatchException;
import com.layduo.common.exception.user.UserPasswordRetryLimitExceedException;
import com.layduo.common.utils.StringUtils;
import com.layduo.framework.shiro.service.SysLoginService;
import com.layduo.system.domain.SysUser;/*** 自定义Realm处理登录认证授权* * @author layduo* @createTime 2019年11月8日 上午10:18:12*/
public class UserRealm extends AuthorizingRealm {private static final Logger log = LoggerFactory.getLogger(UserRealm.class);@Autowiredprivate SysLoginService loginService;/*** 授权*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//整合授权时讲述return null;}/*** 用户认证*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {UsernamePasswordToken upToken = (UsernamePasswordToken) token;String username = upToken.getUsername();String password = StringUtils.EMPTY;if (upToken.getPassword() != null) {password = new String(upToken.getPassword());}SysUser user = null;try {user = loginService.login(username, password);} catch (CaptchaException e) {throw new AuthenticationException(e.getMessage(), e);} catch (UserNotExistsException e) {throw new UnknownAccountException(e.getMessage(), e);} catch (UserPasswordNotMatchException e) {throw new IncorrectCredentialsException(e.getMessage(), e);} catch (UserPasswordRetryLimitExceedException e) {throw new ExcessiveAttemptsException(e.getMessage(), e);} catch (UserBlockedException e) {throw new LockedAccountException(e.getMessage(), e);} catch (RoleBlockedException e) {throw new LockedAccountException(e.getMessage(), e);} catch (Exception e) {log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());throw new AuthenticationException(e.getMessage(), e);}SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());return info;}/*** 清理缓存权限*/public void clearCachedAuthorizationInfo() {this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());}}

调用数据库查询实现具体认证过程,开发过程如果想跳过验证码校验,可以在全局配置文件application.yml中,把验证码开关设置为false

package com.layduo.framework.shiro.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import com.layduo.common.constant.Constants;
import com.layduo.common.constant.ShiroConstants;
import com.layduo.common.constant.UserConstants;
import com.layduo.common.enums.UserStatus;
import com.layduo.common.exception.user.CaptchaException;
import com.layduo.common.exception.user.UserBlockedException;
import com.layduo.common.exception.user.UserDeleteException;
import com.layduo.common.exception.user.UserNotExistsException;
import com.layduo.common.exception.user.UserPasswordNotMatchException;
import com.layduo.common.utils.DateUtils;
import com.layduo.common.utils.MessageUtils;
import com.layduo.common.utils.ServletUtils;
import com.layduo.framework.manager.AsyncManager;
import com.layduo.framework.manager.factory.AsyncFactory;
import com.layduo.framework.util.ShiroUtils;
import com.layduo.system.domain.SysUser;
import com.layduo.system.service.ISysUserService;/*** 登录校验方法
* @author layduo
* @createTime 2019年11月11日 上午10:23:46
*/
@Component
public class SysLoginService {@Autowiredprivate SysPasswordService passwordService;@Autowiredprivate ISysUserService userService;/*** 登录* @param username* @param password* @return*/public SysUser login(String username, String password) {//验证码校验String captacha = (String) ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA);if (!StringUtils.isEmpty(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));throw new CaptchaException();}//用户名或密码为空 错误if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));throw new UserNotExistsException();}//用户名不在指定范围内 错误if (username.length() < UserConstants.USERNAME_MIN_LENGTH || username.length() > UserConstants.USERNAME_MAX_LENGTH) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}//密码不在指定范围内 错误if (password.length() < UserConstants.PASSWORD_MIN_LENGTH || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}//查询用户信息SysUser user = userService.selectUserByLoginName(username);//用手机号码登录if (user == null && maybeMobilePhoneNumber(username)) {user = userService.selectUserByPhoneNumber(username);}//用电子邮箱登录if (user == null && maybeEmail(username)) {user = userService.selectUserByEmail(username);}//校验用户不存在if (user == null) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));throw new UserNotExistsException();}//校验用户删除状态if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));throw new UserDeleteException();}//校验用户状态if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark())));throw new UserBlockedException();}//校验用户密码passwordService.validate(user, password);//校验成功,添加登录记录AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));//记录用户登录IP和时间recordLoginInfo(user);return user;}private boolean maybeEmail(String username) {if (!username.matches(UserConstants.EMAIL_PATTERN)) {return false;}return true;}private boolean maybeMobilePhoneNumber(String username) {if (!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN)) {return false;}return true;}/*** 记录登录信息* @param user*/public void recordLoginInfo(SysUser user) {user.setLoginIp(ShiroUtils.getIp());user.setLoginDate(DateUtils.getNowDate());userService.updateUserInfo(user);}
}

对于密码校验,可使用shiro自带的校验,在这里,博主使用自定义密码校验。

package com.layduo.framework.shiro.service;import java.util.concurrent.atomic.AtomicInteger;import javax.annotation.PostConstruct;import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import com.layduo.common.constant.Constants;
import com.layduo.common.constant.ShiroConstants;
import com.layduo.common.exception.user.UserPasswordNotMatchException;
import com.layduo.common.exception.user.UserPasswordRetryLimitExceedException;
import com.layduo.common.utils.MessageUtils;
import com.layduo.framework.manager.AsyncManager;
import com.layduo.framework.manager.factory.AsyncFactory;
import com.layduo.system.domain.SysUser;/*** 登录密码处理方法* * @author layduo* @createTime 2019年11月8日 上午10:36:19*/
@Component
public class SysPasswordService {@Autowiredprivate CacheManager cacheManager;private Cache<String, AtomicInteger> loginRecordCache;@Value(value = "${user.password.maxRetryCount}")private String maxRetryCount;@PostConstructpublic void init() {loginRecordCache = cacheManager.getCache(ShiroConstants.LOGINRECORDCACHE);}public void validate(SysUser user, String password) {String loginName = user.getLoginName();// 使用AtomicInteger类提供原子操作,线程安全AtomicInteger retryCount = loginRecordCache.get(loginName);// 第一次查询缓存不存在,把信息缓存if (retryCount == null) {retryCount = new AtomicInteger(0);loginRecordCache.put(loginName, retryCount);}// 输入密码次数超过最大值if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount)) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount)));throw new UserPasswordRetryLimitExceedException(Integer.valueOf(maxRetryCount).intValue());}// 用户输入密码不匹配if (!matches(user, password)) {AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount)));loginRecordCache.put(loginName, retryCount);throw new UserPasswordNotMatchException();} else {clearLoginRecordCache(loginName);}}/*** 校验用户密码* * @param user* @param newPassword* @return*/public boolean matches(SysUser user, String newPassword) {return user.getPassword().equals(encryptPassword(user.getLoginName(), newPassword, user.getSalt()));}/*** 清除用户信息缓存* * @param username*/public void clearLoginRecordCache(String username) {loginRecordCache.remove(username);}/*** 对用户信息加密* * @param username* @param password* @param salt* @return*/public String encryptPassword(String username, String password, String salt) {return new Md5Hash(username + password + salt).toHex().toString();}public void unlock(String loginName) {loginRecordCache.remove(loginName);}}

与此同时,在校验中我们采用异步方法对用户记录登录信息

package com.layduo.common.config.thread;import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import com.layduo.common.utils.Threads;/*** 线程池配置** @author layduo**/
//@Configuration
public class ThreadPoolConfig {// 核心线程池大小private int corePoolSize = 50;// 最大可创建的线程数private int maxPoolSize = 200;// 队列最大长度private int queueCapacity = 1000;// 线程池维护线程所允许的空闲时间private int keepAliveSeconds = 300;@Bean(name = "threadPoolTaskExecutor")public ThreadPoolTaskExecutor threadPoolTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setMaxPoolSize(maxPoolSize);executor.setCorePoolSize(corePoolSize);executor.setQueueCapacity(queueCapacity);executor.setKeepAliveSeconds(keepAliveSeconds);// 线程池对拒绝任务(无线程可用)的处理策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;}/*** 执行周期性或定时任务*/@Bean(name = "scheduledExecutorService")protected ScheduledExecutorService scheduledExecutorService() {return new ScheduledThreadPoolExecutor(corePoolSize,new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) {@Overrideprotected void afterExecute(Runnable r, Throwable t) {super.afterExecute(r, t);Threads.printException(r, t);}};}
}
package com.layduo.framework.manager;import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;import com.layduo.common.utils.Threads;
import com.layduo.common.utils.spring.SpringUtils;/*** 异步任务管理器* * @author layduo*/
public class AsyncManager {/*** 操作延迟10毫秒*/private final int OPERATE_DELAY_TIME = 10;/*** 异步操作任务调度线程池*/private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");/*** 单例模式*/private AsyncManager() {}private static AsyncManager me = new AsyncManager();public static AsyncManager me() {return me;}/*** 执行任务* * @param task*            任务*/public void execute(TimerTask task) {executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);}/*** 停止任务线程池*/public void shutdown() {Threads.shutdownAndAwaitTermination(executor);}
}

 异步任务管理器调用异步工厂相关任务

package com.layduo.framework.manager.factory;
/*** 异步工厂 (产生任务用)
* @author layduo
* @createTime 2019年11月8日 下午2:20:04
*/import java.util.TimerTask;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.layduo.common.constant.Constants;
import com.layduo.common.utils.AddressUtils;
import com.layduo.common.utils.ServletUtils;
import com.layduo.common.utils.spring.SpringUtils;
import com.layduo.framework.util.LogUtils;
import com.layduo.framework.util.ShiroUtils;
import com.layduo.system.domain.SysLogininfor;
import com.layduo.system.service.impl.SysLogininforServiceImpl;import eu.bitwalker.useragentutils.UserAgent;public class AsyncFactory {private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");/*** 记录登录信息* @param username 用户名* @param status 状态* @param message 消息* @param args 列表* @return 任务task*/public static TimerTask recordLogininfor(final String username, final String status, final String message, final Object... args) {final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));final String ip = ShiroUtils.getIp();return new TimerTask() {@Overridepublic void run() {String address = AddressUtils.getRealAddressByIP(ip);StringBuilder s = new StringBuilder();s.append(LogUtils.getBlock(ip));s.append(address);s.append(LogUtils.getBlock(username));s.append(LogUtils.getBlock(status));s.append(LogUtils.getBlock(message));//打印信息到日志sys_user_logger.info(s.toString(), args);//获取客户端操作系统String os = userAgent.getOperatingSystem().getName();//获取客户端浏览器String browser = userAgent.getBrowser().getName();//封装对象SysLogininfor logininfor = new SysLogininfor();logininfor.setLoginName(username);logininfor.setIpaddr(ip);logininfor.setLoginLocation(address);logininfor.setBrowser(browser);logininfor.setOs(os);logininfor.setMsg(message);//日志状态if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) {logininfor.setStatus(Constants.SUCCESS);}else if (Constants.LOGIN_FAIL.equals(status)) {logininfor.setStatus(Constants.FAIL);}//插入数据SpringUtils.getBean(SysLogininforServiceImpl.class).insertLogininfor(logininfor);}};}
}

在这里,提示一下博主踩过的坑,由于分模块开发,在扫描包名的时候,我们得使用com.layduo.**.domain类似这样的写法,但因为分模块的原因,除了web模块,spring未能扫描其他模块下的domain目录,在此,我们需要添加mybatis的相关扫描配置如下:

package com.layduo.framework.config;import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import javax.sql.DataSource;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;/*** Mybatis支持*匹配扫描包* * @author layduo*/
@Configuration
public class MyBatisConfig {@Autowiredprivate Environment env;static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";public static String setTypeAliasesPackage(String typeAliasesPackage) {ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);List<String> allResult = new ArrayList<String>();try {for (String aliasesPackage : typeAliasesPackage.split(",")) {List<String> result = new ArrayList<String>();aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+ ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/"+ DEFAULT_RESOURCE_PATTERN;Resource[] resources = resolver.getResources(aliasesPackage);if (resources != null && resources.length > 0) {MetadataReader metadataReader = null;for (Resource resource : resources) {if (resource.isReadable()) {metadataReader = metadataReaderFactory.getMetadataReader(resource);try {result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());} catch (ClassNotFoundException e) {e.printStackTrace();}}}}if (result.size() > 0) {HashSet<String> hashResult = new HashSet<String>(result);allResult.addAll(hashResult);}}if (allResult.size() > 0) {typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));} else {throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");}} catch (IOException e) {e.printStackTrace();}return typeAliasesPackage;}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");String mapperLocations = env.getProperty("mybatis.mapperLocations");String configLocation = env.getProperty("mybatis.configLocation");typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);VFS.addImplClass(SpringBootVFS.class);final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);sessionFactory.setTypeAliasesPackage(typeAliasesPackage);sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));return sessionFactory.getObject();}
}

此外还有添加mapper扫描,想为了方便也可以在启动类上添加如下注解:

package com.layduo.framework.config;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;/*** 程序注解配置** @author layduo*/
@Configuration
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("com.layduo.**.mapper")
public class ApplicationConfig {}

四、测试shiro认证过程

package com.layduo.web.controller;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;import com.layduo.common.core.controller.BaseController;
import com.layduo.common.core.domain.AjaxResult;
import com.layduo.common.utils.ServletUtils;
import com.layduo.common.utils.StringUtils;/*** 登录验证* * @author layduo*/
@Controller
public class SysLoginController extends BaseController {@GetMapping("/login")public String login(HttpServletRequest request, HttpServletResponse response) {// 如果是Ajax请求,返回Json字符串。if (ServletUtils.isAjaxRequest(request)) {return ServletUtils.renderString(response, "{\"code\":\"1\",\"msg\":\"未登录或登录超时。请重新登录\"}");}return "login";}@PostMapping("/login")@ResponseBodypublic AjaxResult ajaxLogin(String username, String password, Boolean rememberMe) {UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);Subject subject = SecurityUtils.getSubject();try {subject.login(token);return success();} catch (AuthenticationException e) {String msg = "用户或密码错误";if (StringUtils.isNotEmpty(e.getMessage())) {msg = e.getMessage();}return error(msg);}}@GetMapping("/unauth")public String unauth() {return "error/unauth";}
}

在没有进行用户信息校验成功的情况下,shiro都会将请求给设定的/login,提示用户进行用户信息认证:

 待用户进行登录信息认证通过后,shiro将跳转至设定的主页面/index

到这里,有关shiro的用户认证流程就讲完了,项目源码已上传github: https://github.com/builthuLin/layduo.git  有需要的自己fork一下~ ,想更深了解的小伙伴可百度若依框架进行自行学习和深入研究!

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

相关文章

  1. 蛇形矩阵实现算法

    题目:在n*n方阵里填入1,2,...,n*n,要求填成蛇形。例如n=4时方阵为:10 11 12 19 16 13 28 15 14 37 6 5 4package com.copycat.test; import java. util.*; public class Main {public static void main(String[] args) {Scanner s = new Scanner(System.in);in…...

    2024/4/14 22:43:49
  2. RedHat7.2设置NTP服务器及客户端同步时间

    在配置时钟同步服务器时第一次同步时间时,使用ntpdate命令;后续通过ntpd服务与服务器同步时间。一、搭建NTP服务器1、查看服务器、客户端操作系统版本[root@server1 bin]# cat /etc/redhat-release Red Hat Enterprise Linux Server release 7.2 (Maipo)2、查看服务器是否安装…...

    2024/5/8 20:50:22
  3. 一招教你把Win10输入法切换改成与Win7一模一样(Ctrl + 空格)

    使用Win10系统的你,有没有发觉它切换输入法的方式改变了,变得和Win7的切换方式都不一样了,你没法使用Ctrl+空格在输入法/非输入法之间切换,也找不到Win7下缺省的美式英文键盘。即使在安装了多种第三方中文输入法后,你仍然无法像Win7那样使用Ctrl+空格来切换中英文键盘。非…...

    2024/4/19 15:00:36
  4. 16进制及2进制转换

    十六进制优势所在: 1 与二进制转换方便,记住:8、4、2、1. "8421" 2 数字长度比二进制短,与10进制相当。 实际上在计算机内部使用的数字只有2进制的,也就是说只有0和1两个数字而已。 而16进制是计算机使用人员为了更好的表达计算机中存储的数字而使用的,可以想…...

    2024/5/8 17:36:50
  5. 搞定微服务线上生命周期管理,同时发布上千个服务节点不是事儿

    当微服务完成开发、测试后,就可以通过发布服务将其发布到线上。如果只看一个服务节点的部署,貌似是一项非常简单的工作,但如果同时发布成百上千个服务节点,尤其是需要在不影响线上业务的前提下完成发布工作,就会变得比较复杂。批量发布是风险度较高的事情,很大一部分线上…...

    2024/4/14 22:43:46
  6. Centos利用 git node shell 实现持续集成

    目录前言准备工作创建springboot工程在github上创建一个仓库编写node 的监听服务器编写git.sh脚本最后测试 前言 昨天听完老师的课,想自己弄一下持续集成,从五点做到十一点,走了不少弯路,这里记录一下实现步骤和,实现心得 准备工作安装jdk#用yum去安装jdk yum install jav…...

    2024/4/17 0:11:38
  7. Shiro 学习笔记(7)—— Shiro 集成 Web

    这一节介绍了在 Web 项目中使用 Shiro 的步骤。Shiro 学习笔记7 Shiro 集成 Web 步骤1在 webxml 部署描述符中配置 Shiro 步骤2配置 shiroini 步骤3编写登录认证的代码Shiro 学习笔记(7)—— Shiro 集成 Web在 Web 环境下集成 Shiro 其实不难,按照官方文本的说明实现就可以了…...

    2024/4/14 22:43:43
  8. MFC--十进制与十六进制之间的转换

    CString str="1FF";//十六进制值 int n=strtol(str,NULL,16);//转换为10进制 int m=n+1;//你对数据的操作 str.Format("%X",m);//转化为十六进制...

    2024/4/14 22:43:47
  9. 企业内部在centos7.2系统中必杀技NTP时间服务器及内网服务器时间同步(windows和linux客户端同步)

    网络时间协议NTP(Network Time Protocol)是用于互联网中时间同步的标准互联网协议。NTP的用途是把计算机的时间同步到某些时间标准。目前采用的时间标准是世界协调时UTC(Universal Time Coordinated)。NTP的主要开发者是美国特拉华大学的David L. Mills教授。 NTP对于我们个…...

    2024/4/15 23:16:25
  10. 解压版/免安装版MySQL配置全解

    解压版/免安装版MySQL配置全解<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 一 下载MySQL http://dev.mysql.com/downloads/mysql/5.0.html 解压MySQL(F:/Program Files/MySQL/)MySQL安装目录 二 新建my.ini 在F:/Progra…...

    2024/4/14 21:59:17
  11. Fleury算法求欧拉路径

    Fleury算法求欧拉路径 列出一些有关欧拉的题 混合图欧拉回路 poj1637,zju1992,hdu3472 1HDU 3018 Ant Trip 2POJ1041 Johns trip 3 POJ1386 Play on Words 4 POJ2230 Watch Cow5 POJ 2513 Colored Sticks 6 POJ2337 Catenyms 7 POJ1392 Ouroboros Snake 8 HDU2894 DeBruijin邮…...

    2024/4/14 21:59:16
  12. 重装Win7系统步骤和详细教程

    重装win7系统?现在来说越来越多的朋友都喜欢自己来重装系统,喜欢动手,但是依旧有很多朋友不会重装,虽然想操作,但奈何没有人教,那么这里将给这些朋友一个详细的重装win7系统的教程,一步一步的安装教程进行操作,自己重装简直不要太简单。好,下面我们进入到正题了(敲黑…...

    2024/4/14 21:59:15
  13. Tomcat启动乱码

    Tomcat启动乱码1、找到安装的tomcat的conf目录 2、找到logging.properties配置文件 3、在文件中找到 java.util.logging.ConsoleHandler.encoding = utf-8这行 4、将utf-8修改为 java.util.logging.ConsoleHandler.encoding = GBK...

    2024/4/14 21:59:14
  14. 全网最简单的shiro教程第五节

    Shiro授权:第一种:基于资源的授权,必须提供资源授权码才能访问相应的资源。第二种:基于角色授权,必须获取到角色才能访问资源。shiro认证与授权的关系:认证是授权的前提,通过了认证才能执行授权。Java实现shiro认证流程的基本步骤:授权方法:自定义realm中的授权方法:…...

    2024/4/14 21:59:14
  15. Python-OpenCV学习(十一)分水岭算法进行图像分割

    分水岭算法进行图像分割:分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成…...

    2024/4/14 21:59:12
  16. 配置ntp客户端与服务器端时间的同步

    1,实验机器介绍Ip地址服务器1192.168.245.128服务器2192.168.245.130客户端1192.168.245.129实验前准备在ntpS1 和ntpS2 中,配置外部服务器为同步服务器,并开放给192.168.245.0/24网段同步。server 210.72.145.44 perfer # 指定要同步的网络服务器的ip地址server 202.112.…...

    2024/4/14 21:59:11
  17. MySQL免安装版使用配置教程

    最近为了测试某些应用必须要使用数据库,而公司里电脑没有权限安装软件和写注册表,于是就想起了MySQL免安装版,之前在网上找了一些资料,都有些问题,最后尝试了各种方法终于成功的搞出来,现在重新整理下分享大家。首先需要从官网中下载一个免安装的mysql包。为了方便本人已…...

    2024/4/14 21:59:10
  18. pe下如何安装uefi gpt win7系统呢?

    pe下如何安装uefi gpt win7系统呢?uefi gpt启动模式是大家所追求的,因为使用此模式能让电脑速度变快。然而,大多数小白不了解uefi gpt启动模式,所以在u盘装系统常常装机失败,接下来快启动小编带大家了解pe uefi gpt环境下安装win7系统的详细操作教程哦。装机须知:1、想要…...

    2024/4/28 12:05:52
  19. labview中16进制字符串转换位2进制字符串

    在labview中,实现将一个64位的字符串从16进制转换为2进制起初查找许多例程,转换最大长度位32位,研究后进行了一些修改,可以实现长字符串的转换。首先先将64位的长字符串截为两个32位字符串,分别在进行转换,最后连接转换后的两个字符串。为保证转换出二进制字符串的长度,…...

    2024/4/14 22:43:39
  20. 新手小白,买完云服务器该如何使用呢?

    新手小白,买完云服务器该如何使用呢? 看楼主描述的意思,是打算购买服务器来部署网站,那么今天我就分享下部署网站需要做哪些准备工作: 1、网站程序:考虑到照顾小白用户,这里要说一下需要先有网站程序;必须有一套完整的网站程序,并且了解网站程序所用的开发语言及使用的…...

    2024/4/18 17:26:27

最新文章

  1. springMVC入门学习

    目录 1、 什么是springmvc 2、springmvc工作流程 3、 springmvc快速入门&#xff08;XML版本&#xff09; 4、加载自定义目录下的springmvc.xml配置文件 5、 解析器InternalResourceViewResolver 6、 映射器BeanNameUrlHandlerMapping 7、 适配器SimpleControllerHandle…...

    2024/5/8 23:21:52
  2. 梯度消失和梯度爆炸的一些处理方法

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

    2024/5/7 10:36:02
  3. 使用Nginx搭载文件服务器

    1.进入nginx.conf的配置 带有alias别名的配置 location /root {alias /home/icore;autoindex on; } 带有别名&#xff1a;alias的路径 当访问ip:port/root 默认会直接进入到/home/icore , 浏览器显示的是/home/icore目录下的内容&#xff0c; 不带alias别名使用root的配置 …...

    2024/5/4 3:01:42
  4. 从头开发一个RISC-V的操作系统(二)RISC-V 指令集架构介绍

    文章目录 前提ISA的基本介绍ISA是什么CISC vs RISCISA的宽度 RISC-V指令集RISC-V ISA的命名规范模块化的ISA通用寄存器Hart特权级别内存管理与保护异常和中断 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提…...

    2024/5/5 1:33:57
  5. K8S容器空间不足问题分析和解决

    如上图&#xff0c;今天测试环境的K8S平台出现了一个问题&#xff0c;其中的一个容器报错&#xff1a;Free disk space below threshold. Available: 3223552 bytes (threshold: 10485760B)&#xff0c;意思服务器硬盘空间不够了。这个问题怎么产生的&#xff0c;又怎么解决的呢…...

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

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

    2024/5/8 6:01:22
  7. 【原油贵金属周评】原油多头拥挤,价格调整

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

    2024/5/7 9:45:25
  8. 【外汇周评】靓丽非农不及疲软通胀影响

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

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

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

    2024/5/7 14:25:14
  10. 【外汇早评】日本央行会议纪要不改日元强势

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

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

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

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

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

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

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

    2024/5/7 11:36:39
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

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

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

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

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

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

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

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

    2024/5/8 20:48:49
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

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

    2024/5/7 9:26:26
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

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

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

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

    2024/5/8 19:33:07
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

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

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

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

    2024/5/8 20:38:49
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

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

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

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

    2024/5/6 21:42:42
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

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

    2024/5/4 23:54:56
  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