一、禁用shiro原有的session
**(目的是在使用前后端分离,移动端,小程序访问时候没有session)
1.首先自定义类继承DefaultWebSubjectFactory(注:此配置只用于**springboot中有用,在SSM框架中会报错,错误信息:session create ……disabled)
package com.datacraftman.PB.config;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
/**
* @Author WangXuefeng
* @Create 2020-07-25-14:28
* @Description: 禁用session
*/
public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
@Override
public Subject createSubject(SubjectContext context) {
//不创建session
context.setSessionCreationEnabled(false);
return super.createSubject(context);
}
}
2.进行spring-shiro.xml配置
配置如下两个配置防止报错(即:报错信息是session没有能力的错误)
<!--解决报错,组装默认的subjectDAO-->
<bean id="subjectDAO" class="org.apache.shiro.mgt.DefaultSubjectDAO">
<property name="sessionStorageEvaluator" ref="sessionStorageEvaluator"/>
</bean>
<bean id="sessionStorageEvaluator" class="org.apache.shiro.mgt.DefaultSessionStorageEvaluator">
<property name="sessionStorageEnabled" value="false"/>
</bean>
同时还要配置以下两个配置防止报错
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
<!-- 禁用掉会话调度器 -->
<bean id="sessionManager" class="org.apache.shiro.session.mgt.DefaultSessionManager">
<property name="sessionValidationSchedulerEnabled" value="true"/>
</bean>
(切记:如果还报错那种的session disabled的错误一定是这几个配置没有放进SecurityManager容器中,需要在SecurityManager中注入上面的两个配置进去,如下)
<!--安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--注入认证授权realm-->
<property name="realm" ref="customRealm"/>
<!-- 在安全管理器中注入缓存管理器 -->
<!-- <property name="cacheManager" ref="cacheManager"/>-->
<!--解决报错,组装默认的subjectDAO-->
<property name="subjectDAO" ref="subjectDAO"/>
<!-- 禁用掉会话调度器 -->
<property name="sessionManager" ref="sessionManager"/>
</bean>
引入spring-shiro.xml中(ssm不用引入)
<!--配置禁用session-->
<bean id="statelessDefaultSubjectFactory" class="com.datacraftman.PB.config.StatelessDefaultSubjectFactory">(springboot)
二、SHIRO自定义拦截器
既然要整合JWT就要重写自定义的拦截器主要重写的shiro的两个方法
-
onAccessDenied
-
isAccessAllowed
isAccessAllowed方法return true会放行,return false则会调用onAccessDenied方法,如果发现返回true还有报错的话百分之90还是shiro配置的原因,那几个防止shiro配置session后报错的那几个配置。
这里用的JWT工具包写的工具类所以整合的代码如下:
package com.datacraftman.PB.filter;
import com.auth0.jwt.interfaces.Claim;
import com.datacraftman.PB.utils.JwtUtil;
import com.datacraftman.PB.vo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
/**
* @Author WangXuefeng
* @Create 2020-07-25-15:15
* @Description: 自定义session拦截器
*/
public class NoSessionFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
System.out.println("isAccessAllowed");
//强转HttpServletRequest
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
//取token
String token = httpServletRequest.getParameter("token");
//判断token
if("".equals(token) || token == null){
return false;
}
//进行过期验证
try {
JwtUtil.verifyToken(token);
} catch (Exception e) {
return false;
}
//解析token
Map<String, Claim> map = JwtUtil.parseToken(token);
Claim userId = map.get("userId");
//判断用户id
if("".equals(userId.asInt()) || userId.asInt() == null){
return false;
}
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setCharacterEncoding("utf-8");
resp.setContentType("application/json; charset=utf-8");
ObjectMapper mapper = new ObjectMapper();
try {
resp.getWriter().write(mapper.writeValueAsString(new Result("500",null,"未登录!")));
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
3.既然写完类不像springboot那么方便,我们还要将重写好的拦截器配进去以替换shiro默认的拦截器
首先将写好的拦截器注入spring-shiro容器中
<!--自定义拦截规则-->
<bean id="noSessionFilter" class="com.datacraftman.PB.filter.NoSessionFilter"></bean>
然后再ShiroFilterFactoryBean中引用自己写好的类替换原有的
<!-- 引入自定的拦截器-->
<property name="filters">
<map>
<entry key="jwt" value-ref="noSessionFilter"/>
</map>
</property>
最后如下就可以用自己的拦截器了
<!--shiro过滤器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--注入安全管理器-->
<property name="securityManager" ref="securityManager"/>
<!--没有登录的时候,跳转到这个页面-->
<!-- <property name="loginUrl" value="/user/unauth"/>-->
<!-- <!–无权限跳转页面–>-->
<!-- <property name="unauthorizedUrl" value="/user/nopermission"/>-->
<!-- 引入自定的拦截器-->
<property name="filters">
<map>
<entry key="jwt" value-ref="noSessionFilter"/>
</map>
</property>
<!--过滤器链,优先级从上往下-->
<property name="filterChainDefinitions">
<value>
<!--anno表示不需要登录就可以访问-->
/user/login = anon
/user/unauth = anon
/register/* = anon
<!-- 验证码,可匿名访问 -->
<!-- /validatecode.jsp** = anon-->
<!-- /webapp/** = anon-->
<!-- <!–/user/createCode= anon–>-->
<!-- <!–auhtc表示需要认证才能访问–>-->
<!-- /user/readUser = authc,perms[/readUser] <!–perms表示拥有此权限才能访问–>-->
<!-- /user/readBook = authc,perms[vip]-->
/user/* = jwt <!—使用自定义拦截器-->
/** = jwt
</value>
</property>
</bean>