事发现场
将我的网盘项目部署到服务器上之后,第一次加载时不显示静态资源,但是在本地测试时,是可以显示的,可能是本地缓存的原因,当时并没有发现这个问题。
于是我关闭浏览器重新打开了本地项目,问题出现了,没有网页样式。是不是没有请求到资源?F12一看的确是这样,它报了400错误,请求的url是错的,当然找不到资源了
正常的请求应该是http://localhost:8080/css/xxxxx
刷新页面又正常了
所以解决问题的关键点就在于这个jsessionid
解决方式
方式一
我在首页添加了令牌的验证,在Shiro第一次重定向时,会重写url,带有jsessionid,它就是sessionid,是servlet容器(tomcat)用来记录用户session的。
由于这个网盘是基于SpringBoot写的,tomcat内嵌在里头,所以我首先想到的是修改yml配置,指定会话跟踪模式为cookie
# 指定会话跟踪模式为cookie
servlet:
session:
tracking-modes: 'cookie'
方式二
在Shiro配置类中指定会话跟踪模式
@Bean
public ServletContextInitializer servletContextInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE) );
}
};
}
方式三
我在ShiroHttpSession中找到了这个常量
ShiroHttpServletResponse和DefaultWebSessionManager都引用了这个常量
首先去ShiroHttpServletResponse这个类里看下
private static final String DEFAULT_SESSION_ID_PARAMETER_NAME = ShiroHttpSession.DEFAULT_SESSION_ID_NAME;
它将DEFAULT_SESSION_ID_NAME重新定义成了DEFAULT_SESSION_ID_PARAMETER_NAME,就是JSESSIONID
它被引用到两个方法里,doIsEncodeable和toEncoded
在执行doIsEncodeable方法之前,它还会在isEncodeable这个方法里判断是否需要编码
1.url重写是否全局禁用,能不能在url加session
2.有没有session
3.request的cookie有没有session
protected boolean isEncodeable(final String location) {
// First check if URL rewriting is disabled globally
if (Boolean.FALSE.equals(request.getAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED)))
return (false);
if (location == null)
return (false);
// Is this an intra-document reference?
if (location.startsWith("#"))
return (false);
// Are we in a valid session that is not using cookies?
final HttpServletRequest hreq = request;
final HttpSession session = hreq.getSession(false);
if (session == null)
return (false);
if (hreq.isRequestedSessionIdFromCookie())
return (false);
return doIsEncodeable(hreq, session, location);
}
满足条件后会执行doIsEncodeable,在url后添加jsessionid
String contextPath = getRequest().getContextPath();
if (contextPath != null) {
String file = url.getFile();
if ((file == null) || !file.startsWith(contextPath))
return (false);
String tok = ";" + DEFAULT_SESSION_ID_PARAMETER_NAME + "=" + session.getId();
if (file.indexOf(tok, contextPath.length()) >= 0)
return (false);
}
// This URL belongs to our web application, so it is encodeable
return (true);
toEncoded也是一种编码方式,但只需要两个参数url和sessionid
protected String toEncoded(String url, String sessionId) {
if ((url == null) || (sessionId == null))
return (url);
String path = url;
String query = "";
String anchor = "";
int question = url.indexOf('?');
if (question >= 0) {
path = url.substring(0, question);
query = url.substring(question);
}
int pound = path.indexOf('#');
if (pound >= 0) {
anchor = path.substring(pound);
path = path.substring(0, pound);
}
StringBuilder sb = new StringBuilder(path);
if (sb.length() > 0) { // session id param can't be first.
sb.append(";");
sb.append(DEFAULT_SESSION_ID_PARAMETER_NAME);
sb.append("=");
sb.append(sessionId);
}
sb.append(anchor);
sb.append(query);
return (sb.toString());
}
再看DefaultWebSessionManager这个类,在它的构造方法里,发现了
this.sessionIdUrlRewritingEnabled = false;
public DefaultWebSessionManager() {
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
cookie.setHttpOnly(true); //more secure, protects against XSS attacks
this.sessionIdCookie = cookie;
this.sessionIdCookieEnabled = true;
this.sessionIdUrlRewritingEnabled = false;
}
也就是说,我在Shiro配置类里初始化一个DefaultWebSessionManager就好了,因为它的sessionIdUrlRewritingEnabled默认是false
@Bean
public DefaultWebSecurityManager securityManager(@PathVariable("userRealm") UserRealm userRealm,
@PathVariable("sessionManager") DefaultWebSessionManager sessionManager){
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
manager.setRealm(userRealm);
sessionManager.setSessionIdUrlRewritingEnabled(false);
manager.setSessionManager(sessionManager);
manager.setRememberMeManager(manager.getRememberMeManager());
return manager;
}
@Bean
public DefaultWebSessionManager sessionManager(){
return new DefaultWebSessionManager();
}