springboot 整合 shrio 和 token 记录

  1. shrio 整合中用到的几个 概念  

      1.1  realm 一般就是权限验证   很好理解 一般继承 AuthorizingRealm 类 实现自己的登录逻辑

      1.2 SessionManager 会话管理器 主要功能 管理创建session   sessionFactory   管理session 的缓存   cacheManager  

    1.3 SecurityManager 安全管理器  一般注入 自己的realm实现   自己的sessionManager实现

    1.4 ShiroFilterFactoryBean 过滤器 主要匹配 URL 和 shrio 过滤器
2.具体实现 自定义realm
  
package com.xhc.framework.config.shiro.realm;


import com.xhc.common.utils.RandomUtil;
import com.xhc.system.entity.SysUsers;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 自定义Realm 处理登录 权限
 * 
 * @author ruoyi
 */
public class UserRealm extends AuthorizingRealm
{
    private static final Logger log = LoggerFactory.getLogger(UserRealm.class);


    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)
    {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRole("admin");
        info.addStringPermission("*:*:*");
        return info;
    }

    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
    {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        final char[] password1 = upToken.getPassword();
        String password = "";
        SysUsers user=new SysUsers();
        user.setLoginAccount(username);
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password1, getName());
        return info;
    }

    /**
     * 清理缓存权限
     */
    public void clearCachedAuthorizationInfo()
    {
        this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
    }
}

  

注入自定义realm
@Bean
    public SecurityManager securityManager(UserRealm userRealm)
    {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(userRealm);
        
        return securityManager;
    }

  3.自定义sessionFactory会话 创建session

package com.xhc.framework.config.shiro.session;

import javax.servlet.http.HttpServletRequest;

import com.xhc.common.utils.IpUtils;
import com.xhc.common.utils.RandomUtil;
import com.xhc.common.utils.ServletUtils;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.session.mgt.SessionFactory;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.web.session.mgt.WebSessionContext;
import org.springframework.stereotype.Component;


/**
 * 自定义sessionFactory会话
 * 
 * @author ruoyi
 */
@Component
public class OnlineSessionFactory implements SessionFactory
{
    @Override
    public Session createSession(SessionContext initData)
    {



        if (initData != null) {
            String host = initData.getHost();
            if (host != null) {
                OnlineSession  session = new OnlineSession(); //自己继承的session 类 扩招功能
                session.setHost(host);
                System.out.println("创建sessionid"+session.toString());
                return session;
            }
        }
        System.out.println("创建sessionid initData=null");
        return new OnlineSession();

    }
}

  注入自己 实现

  @Bean
    public SessionManager sessionManager(){
        ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        
        // 自定义sessionFactory
        shiroSessionManager.setSessionFactory(sessionFactory());
        
        return shiroSessionManager;
    }

  4.自定义缓存的实现   继承 EnterpriseCacheSessionDAO

package com.xhc.framework.config.shiro.session;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;

import java.io.Serializable;

/**
 * 针对自定义的ShiroSession的db操作
 * 
 * @author ruoyi
 */
public class OnlineSessionDAO extends EnterpriseCacheSessionDAO
{
    @Override
    protected Serializable doCreate(Session session) {
        System.out.println("doCreate"+session.toString());
        return super.doCreate(session);
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        System.out.println("doReadSession sessionId"+sessionId);
        return super.doReadSession(sessionId);
    }

    @Override
    protected void doUpdate(Session session) {
        System.out.println("doUpdate"+session.toString());
        super.doUpdate(session);
    }

    @Override
    protected void doDelete(Session session) {
        System.out.println("doDelete"+session.toString());
        super.doDelete(session);
    }
}

注入配置中

 @Bean
    public SessionManager sessionManager(){
        ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        //这里可以不设置。Shiro有默认的session管理。如果缓存为Redis则需改用Redis的管理
       shiroSessionManager.setSessionDAO(sessionDAO());
     
        return shiroSessionManager;
    }

  

5.配置 shiro 过滤器 (url和过滤器匹配 自定义过滤器)

@Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
    {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 身份认证失败,则跳转到登录页面的配置
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        // 权限认证失败,则跳转到指定页面
        shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
        // Shiro连接约束配置,即过滤链的定义
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 对静态资源设置匿名访问

        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/templates/**", "anon");
        // 退出 logout地址,shiro去清除session
        filterChainDefinitionMap.put("/system/loginOut", "anon");
        // 不需要拦截的访问
        filterChainDefinitionMap.put("/system/login", "anon");
        // 系统权限列表
        // filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());


        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();

      //这里是自己实现的过滤器 注入的配置汇中 filters.put(
"kickout", kickoutSessionFilter()); // 注销成功,则跳转到指定页面 shiroFilterFactoryBean.setFilters(filters); // 所有请求需要认证 filterChainDefinitionMap.put("/**", "kickout"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
anon 默认的不需要登录的过滤器
filters.put("kickout", kickoutSessionFilter()); 是自己实现的过滤器
先注入过滤器实现 再去匹配
filterChainDefinitionMap.put("/**", "kickout");

自定义过滤器
package com.xhc.framework.config.shiro;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xhc.common.vo.AjaxResult;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 登录帐号控制过滤器
 * 可以发现他是调用的isAccessAllowed方法和onAccessDenied方法,只要两者有一个可以就可以了,从名字中我们也可以理解,他的逻辑是这样:先调用isAccessAllowed,如果返回的是true,
 * 则直接放行执行后面的filter和servlet,如果返回的是false,则继续执行后面的onAccessDenied方法,如果后面返回的是true则也可以有权限继续执行后面的filter和servelt。
 * 只有两个函数都返回false才会阻止后面的filter和servlet的执行
 *
 * @author ruoyi
 */
public class KickoutSessionFilter extends AccessControlFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {

        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
         String requestURI = httpRequest.getRequestURI();
         String requestURL = httpRequest.getRequestURL().toString();
        Subject subject = getSubject(request, response);
        System.out.println("sessionid:"+subject.getSession().getId());
        System.out.println("requestURI:"+requestURI);
        System.out.println("requestURL:"+requestURL);

       //已结登录 或者 记住密码
        if (!subject.isAuthenticated() && !subject.isRemembered()) {
            // 如果没有登录或用户最大会话数为-1,直接进行之后的流程
            httpResponse.setContentType("application/json");
            httpResponse.setCharacterEncoding("utf-8");
            httpResponse.getWriter().print( new ObjectMapper().writeValueAsString(new AjaxResult().noPermission()));
            return false;
        }
        // 当前登录用户
        return true;
    }


}

 6.如果想用token 更换cookie 的 验证 实现获取sessionid的逻辑 从 request 或者 head 头中获取 token

  

package com.xhc.framework.config.shiro;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

public class ShiroSessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "token";
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {

        String id = WebUtils.toHttp(request).getParameter(AUTHORIZATION);
      //  String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        System.out.println("getSessionId "+id);

        if (id!=null&&!id.equals("")) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则按默认规则从cookie取sessionId
         //  return super.getSessionId(request, response);

           request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        }
    }

}

注入配置中

 @Bean
    public SessionManager sessionManager(){
        ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        //这里可以不设置。Shiro有默认的session管理。如果缓存为Redis则需改用Redis的管理
       // shiroSessionManager.setSessionDAO(sessionDAO());
        // 自定义sessionFactory
        shiroSessionManager.setSessionFactory(sessionFactory());
        // 加入缓存管理器
        shiroSessionManager.setCacheManager(getEhCacheManager());
        return shiroSessionManager;
    }

原文地址:https://www.cnblogs.com/bug1024/p/11263730.html