用户A在线,管理员在后台更改了用户A信息(资料或权限)以后;用户A再进行下一步操做时,会被拦截并退出登陆状态,再登陆才能够执行操做;来确保用户A的信息同步更新。html
版本升级及内容优化版本,改动内容:java
前篇: linux
基于前篇,新增功能:nginx
wyait-manage、wyait-manage-1.2.0源码都更新了以上功能!git
以及新增了springboot项目,开发和线上jdk版本不一致致使项目没法启动、没法加载的问题的排查及解决思路。github
后篇: web
github源码: https://github.com/wyait/manage.git
码云:https://gitee.com/wyait/manage.git
github对应项目源码目录:wyait-manage-1.2.0
码云对应项目源码目录:wyait-manage-1.2.0 redis
使用shiro可能会遇到修改了用户权限后,没有当即生效,须要等到用户从新登陆后才能生效;不能当即同步更新,显然是不合理的。spring
【实测无效】!!!
受权方法,是在shiro进行鉴权的时候才能触发。只是配置了authc/user/anon等,不会触发;
perms,port,rest,roles,ssl等,会触发受权方法doGetAuthorizationInfo。 数据库
/** * 清除全部缓存 */ public void clearCachedAuth(){ this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals()); }
//清除ehcache中全部用户权限缓存 RealmSecurityManager rsm = (RealmSecurityManager)SecurityUtils.getSecurityManager(); ShiroRealm authRealm = (ShiroRealm)rsm.getRealms().iterator().next(); authRealm.clearCachedAuth();
实际解决方案参考下文中的方案二!
在系统中,由管理员更改了用户A信息后,若是用户A在线,没法及时更新相关的改动;
更新用户资料、权限等信息,若是该用户在线,同步更新用户信息解决方案:
在ShiroRealm中经过SessionDAO拿到全部在线的用户,
Collection<Session> sessions = sessionDAO.getActiveSessions();
遍历找到匹配的,根据状况,退出登陆或更新用户信息:
@Autowired private SessionDAO sessionDAO; public void updateShiroUser(String loginName){ Collection<Session> sessions = sessionDAO.getActiveSessions(); for(Session session:sessions){ if(loginName.equals(String.valueOf(session.getAttribute(DefaultSubjeContext.PRINCIPALS_SESSION_KEY))) { //设置session当即失效,即将其踢出系统 session.setTimeout(0); //TODO 或更新下用户信息 break; } }
【不推荐理由】
用户信息新增version版本标记,写个拦截器,每次请求判断version是否一致,若有改动,根据状况,退出或更新用户信息(本文统一作了退出登陆处理,能够结合实际需求作相应调整)。
这个方案,基于乐观锁原理实现。一样可解决动态更新用户权限的问题。
ALTER TABLE `user` MODIFY COLUMN `id` int(10) NOT NULL AUTO_INCREMENT FIRST , ADD COLUMN `version` int(10) NULL DEFAULT 0 COMMENT '更新版本' AFTER `send_time`;
TODO【详见源码】
/** * * @项目名称:wyait-manage * @类名称:UserActionInterceptor * @类描述:判断用户信息是否已被后台更改,并根据更改的状况作对应的处理 * @建立人:wyait * @建立时间:2018年5月2日 上午9:36:43 * @version: */ public class UserActionInterceptor implements HandlerInterceptor { private static Logger logger = LoggerFactory .getLogger(UserActionInterceptor.class); @Autowired private UserService userService; ... ... @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj, Exception e) throws Exception { // TODO Auto-generated method stub } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView mv) throws Exception { // TODO Auto-generated method stub } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception { // TODO Auto-generated method stub logger.debug("请求到达后台方法以前调用(controller以前)"); // 1. SecurityUtils获取session中的用户信息 // HttpSession session=request.getSession(); User user = (User) SecurityUtils.getSubject().getPrincipal(); if (user != null && StringUtils.isNotEmpty(user.getMobile()) && null != user.getVersion()) { // 2. 获取数据库中的用户数据 User dataUser = this.userService.findUserByMobile(user.getMobile()); // 3. 对比session中用户的version和数据库中的是否一致 if (dataUser != null && null != dataUser.getVersion() && String.valueOf(user.getVersion()).equals( String.valueOf(dataUser.getVersion()))) { // 3.1 同样,放行 return true; }else{ // 3.2 不同,这里统一作退出登陆处理;//TODO 使用redis缓存用户权限数据,根据用户更新、用户权限更新;作对应的处理。 SecurityUtils.getSubject().logout(); isAjaxResponse(request,response); } } return false; } ... ... }
/** * * @项目名称:wyait-manage * @类名称:MyWebMvcConfig * @类描述:自定义静态资源映射路径和静态资源存放路径 * @建立人:wyait * @修改时间:2018年5月3日09:55:23 * @version: */ @Configuration public class MyWebMvcConfig extends WebMvcConfigurerAdapter { /** * 添加拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { // 路径根据后期项目的扩展,进行调整 registry.addInterceptor(new UserActionInterceptor()) .addPathPatterns("/user/**", "/auth/**") .excludePathPatterns("/user/sendMsg", "/user/login"); super.addInterceptors(registry); } }
错误信息:
java.lang.NullPointerException: null at com.wyait.manage.interceptor.UserActionInterceptor.preHandle(UserActionInterceptor.java:62) ~[classes/:?] at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:133) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:962) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
userService对象为null,没法注入UserService。
@Autowired private UserService userService;
@Configuration public class MyWebMvcConfig extends WebMvcConfigurerAdapter { /** * * @描述:在Spring添加拦截器以前先建立拦截器对象,这样就能在Spring映射这个拦截器前,把拦截器中的依赖注入的对象给初始化完成了。 * </br>避免拦截器中注入的对象为null问题。 * @建立人:wyait * @建立时间:2018年5月3日 上午10:07:36 * @return */ @Bean public UserActionInterceptor userActionInterceptor(){ return new UserActionInterceptor(); } /** * 添加拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { // 路径根据后期项目的扩展,进行调整 registry.addInterceptor(userActionInterceptor()) .addPathPatterns("/user/**", "/auth/**") .excludePathPatterns("/user/sendMsg", "/user/login"); super.addInterceptors(registry); } }
分布式或集群的时候,须要解决session共享问题;相关的方案有:session持久化、redis或其余中间件、nginx的ip_hash、cookie实现、服务器间Session同步等;这时候处理动态更新用户信息,须要结合实际状况而定;
后期会更新redis版本。
linux 6.* 系统
jdk 1.7
tomcat 7.*
maven 3.3.3
五月 08, 2018 11:49:23 上午 org.apache.catalina.startup.TaglibUriRule body 信息: TLD skipped. URI: http://shiro.apache.org/tags is already defined 五月 08, 2018 11:49:23 上午 org.apache.catalina.startup.TldConfig execute 信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time. 五月 08, 2018 11:51:03 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom 信息: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [112,917] milliseconds. 五月 08, 2018 11:51:03 上午 org.apache.catalina.startup.HostConfig deployDirectory 信息: Deployment of web application directory /usr/tools/tomcat-9190/webapps/ROOT has finished in 117,517 ms 五月 08, 2018 11:51:03 上午 org.apache.catalina.startup.Catalina start 信息: Server startup in 117563 ms 五月 08, 2018 11:51:37 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom 信息: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [134,439] milliseconds. ...
无明显错误,可是项目没有加载成功,访问是404.
At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
对比本地tomcat7和线上tomcat7启动,会出现tomcat7和8版本冲突问题,本地解决了,线上依旧启动不成功;再百度、google方案,大体是tomcat版本问题而打印的日志信息,按照搜索的方案配置,均未解决。
考虑应该是环境配置的版本不一致致使的项目没法加载启动成功,确认并排查下开发、测试、线上的jdk、tomcat等版本是否一致。
java -version
jdk 1.8
java -v
jdk 1.7
这就是致使项目在linux系统启动不起来的缘由:开发和线上的jdk版本不一致!!!
将windows的jdk版本切换为jdk1.7,从新打开新的dos窗口:java -version;jdk显示为1.7.*。版本切换成功!
... [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3. 1:compile (default-compile) on project wyait-manage: Fatal error compiling: 无效的 目标发行版: 1.8 -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e swit ch. ...
<!-- <profile> <id>jdk-1.8</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile> -->
注释掉这一段maven指定jdk为1.8版本的配置;从新打包,成功!
开发过程当中,要确保开发、测试、线上配置的环境(jdk、maven、tomcat等开发依赖的环境支持)保持一致。避免出现因为开发环境中的版本不一致而出现问题,致使项目上线出问题和延迟项目上线时间!
版本升级及内容优化版本,改动内容:
前篇:
后篇:
项目源码:(包含数据库源码)
github源码: https://github.com/wyait/manage.git
码云:https://gitee.com/wyait/manage.git
github对应项目源码目录:wyait-manage-1.2.0
码云对应项目源码目录:wyait-manage-1.2.0