shiro问题
No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton.
This is an invalid application configuration.
是因为我在前面的过滤器 response.sendError(403);导致在拦截器中Subject subject = SecurityUtils.getSubject();报错
Shiro重定向问题 ;jsessionid= 访问404
1.重写几个类
1. ShiroFilterFactoryBean
package com.standards.config;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.BeanInitializationException;
/**
* @Author: hekaikai
* @Date: 2021/6/3 13:30
*/
public class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean {
@Override
public Class getObjectType() {
return MySpringShiroFilter.class;
}
@Override
protected AbstractShiroFilter createInstance() throws Exception {
SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
FilterChainManager manager = createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new MySpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
}
2. AbstractShiroFilter
package com.standards.config;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: hekaikai
* @Date: 2021/6/3 13:30
*/
public class MySpringShiroFilter extends AbstractShiroFilter {
protected MySpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
super();
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
}
setSecurityManager(webSecurityManager);
if (resolver != null) {
setFilterChainResolver(resolver);
}
}
@Override
protected ServletResponse wrapServletResponse(HttpServletResponse orig, ShiroHttpServletRequest request) {
return new MyShiroHttpServletResponse(orig, getServletContext(), request);
}
}
3. ShiroHttpServletResponse
package com.standards.config;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.ShiroHttpServletResponse;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: hekaikai
* @Date: 2021/6/3 13:28
*/
public class MyShiroHttpServletResponse extends ShiroHttpServletResponse {
public MyShiroHttpServletResponse(HttpServletResponse wrapped, ServletContext context, ShiroHttpServletRequest request) {
super(wrapped, context, request);
}
@Override
public String encodeRedirectURL(String url){
/** 下面是ShiroHttpServletResponse源码,重写shiro的encodeRedirectURL方法,把url路径里的JSESSIONID去掉 **/
// if (isEncodeable(toAbsolute(url))) {
// return toEncoded(url, request.getSession().getId());
// } else {
// return url;
// }
return url;
}
@Override
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);
/** 下面是ShiroHttpServletResponse源码,重写shiro的toEncoded方法使其不拼接JSESSIONID **/
// if (sb.length() > 0) { // session id param can't be first.
// sb.append(";");
// sb.append("JSESSIONID");
// sb.append("=");
// sb.append(sessionId);
// }
sb.append(anchor);
sb.append(query);
return (sb.toString());
}
}
2.修改配置
替换之前的 ShiroFilterFactoryBean
<!--<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">-->
<bean id="shiroFilter" class="com.standards.config.MyShiroFilterFactoryBean">
</bean>
shiro 控制同一个账号只能登录一次
添加一个过滤器
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;
@Slf4j
public class KickoutSessionControlFilter extends AccessControlFilter {
private String kickoutUrl; //踢出后到的地址
private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
private int maxSession = 1; //同一个帐号最大会话数 默认1
private Cache<String, Deque<Session>> cache;
public void setKickoutUrl(String kickoutUrl) {
this.kickoutUrl = kickoutUrl;
}
public void setKickoutAfter(boolean kickoutAfter) {
this.kickoutAfter = kickoutAfter;
}
//设置Cache的key的前缀
public void setCacheManager(CacheManager cacheManager) {
this.cache = cacheManager.getCache("shiro_redis_cache:");
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response){
Subject subject = getSubject(request, response);
if (!subject.isAuthenticated() && !subject.isRemembered()) {
//如果没有登录,直接进行之后的流程
return true;
}
Session session = subject.getSession();
//如果之前被标记过就退出
if (session.getAttribute("kickout") != null && (Boolean) session.getAttribute("kickout")) {
subject.logout();
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
try {
httpServletResponse.sendRedirect(kickoutUrl);
} catch (IOException e) {
}
return false;
}
String username = (String) subject.getPrincipal();
Serializable sessionId = session.getId();
Deque<Session> deque = null;
//读取缓存 没有就存入
try{
deque = cache.get(username);
}catch(Exception e){}
//如果此用户没有session队列,也就是还没有登录过,缓存中没有
//就new一个空队列,不然deque对象为空,会报空指针
if (deque == null) {
deque = new LinkedList<Session>();
cache.put(username, deque);
}
//如果队列里没有此sessionId,且用户没有被踢出;放入队列
boolean whetherPutDeQue = true;
if (deque.isEmpty()) {
whetherPutDeQue = true;
} else {
for (Session sessionInqueue : deque) {
if (sessionId.equals(sessionInqueue.getId())) {
whetherPutDeQue = false;
break;
}
}
}
if (whetherPutDeQue) {
deque.push(session);
}
//如果队列里的sessionId数超出最大会话数,开始踢人
while (deque.size() > maxSession) {
Session kickoutSession = null;
if (kickoutAfter) {
//如果踢出后者
kickoutSession = deque.removeFirst();
log.info("踢出后登录的,被踢出的sessionId为: {}", kickoutSession.getId());
} else {
//否则踢出前者
kickoutSession = deque.removeLast();
log.info("踢出先登录的,被踢出的sessionId为: {}", kickoutSession.getId());
}
//如果有踢出的session,标记,下一次直接退出
if (kickoutSession != null) {
kickoutSession.setAttribute("kickout", true);
// subject.logout();
// try {
// WebUtils.issueRedirect(request, response, kickoutUrl);
// } catch (IOException e) {
// e.printStackTrace();
// }
// return false;
}
}
return true;
}
}
在shiro配置文件中添加过滤器
<bean id="kickoutSessionControlFilter" class="com.xxxx.KickoutSessionControlFilter" >
<property name="cacheManager" ref="shiroEhcacheManager"/>
<property name="kickoutUrl" value="/"/>
</bean>
shiro 无密码登录
使用场景
比如前台传过来 token,验证完 token 以后,使用 token 中的用户名登录。
//解析token获取用户名
String username=token.getUsername("xxxx");
//登录
PrincipalCollection principals = new SimplePrincipalCollection(username, username);
WebSubject.Builder builder = new WebSubject.Builder( request,response);
builder.principals(principals);
builder.authenticated(true);
WebSubject subject = builder.buildWebSubject();
ThreadContext.bind(subject);
获取用户信息
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
String username = (String) subject.getPrincipal();
}