shiro相关

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();
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值