用户A在线,管理员在后台更改了用户A信息(资料或权限)之后;用户A再进行下一步操作时,会被拦截并退出登录状态,再登录才可以执行操作;来确保用户A的信息同步更新。
前篇:
基于前篇,新增功能:
wyait-manage、wyait-manage-1.2.0源码都更新了以上功能!
以及新增了springboot项目,开发和线上jdk版本不一致导致项目无法启动、无法加载的问题的排查及解决思路。
后篇:
github源码: https://github.com/wyait/manage.git
码云:https://gitee.com/wyait/manage.git
github对应项目源码目录:wyait-manage-1.2.0
码云对应项目源码目录:wyait-manage-1.2.0
使用shiro可能会遇到修改了用户权限后,没有立即生效,需要等到用户重新登录后才能生效;不能立即同步更新,显然是不合理的。
【实测无效】!!!
授权方法,是在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
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。