shiro配置 在springboot中前后端分离中,集成shiro认证授权框架

一:介绍

    Apache Shiro是Java的一个安全框架。由于它相对小而简单,现在使用的人越来越多。

    Authentication:身份认证/登录,验证用户是不是拥有相应的身份。

    Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。

二:配置

   springboot application-dev.yml 配置文件

spring:   
  shiro:
    properties:
      authz: false
    session-id-cookie:
      http-only: false
      name: yui2-token
      maxAge: 2592000
    session-dao:
      #expire: 86400 选用globalSessionTimeout这个值
      session-prefix: yui2-sid
    session-mgr:
      globalSessionTimeout: 86400000
    shiro-db-realm:
      authentication-caching-enabled: false
      authorization-caching-enabled: false
      authentication-cache-name: yui2-cache-authc
      authorization-cache-name: yui2-cache-authz

authz:false,表示是否开启后端授权
session-id-cookie.http-only:false,如果为true,浏览器读取不到这个token。
session-id-cookie.name:yui2-token,这个值cookie的名称。
session-id-cookie.maxAge:2592000,这个cookie在浏览器保留时间。
session-dao.session-prefix: sessionId,前缀。
session-mgr.globalSessionTimeout:session过期时间。
shiro-db-realm.authentication-caching-enabled: false。表示是否开启用户信息缓存
shiro-db-realm.authorization-caching-enabled: false。 表示是否开启权限信息缓存
shiro-db-realm.authentication-cache-name: yui2-cache-authc。 表示用户信息缓存名称
shiro-db-realm. authorization-cache-name: yui2-cache-authz。 表示权限信息缓存名称


springboot配置文件,ShiroConfig.java

@Configuration
public class ShiroConfig {
    private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
    
    @Bean("ShiroProperties")
    @ConfigurationProperties("spring.shiro.properties")
    public ShiroProperties shiroProperties(){
        return new ShiroProperties();
    }
    
    @Bean("shiroFilter")
    //@ConfigurationProperties("spring.shiro.shiro-filter")
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, ShiroProperties shiroProperties) throws IOException {
        logger.info("ShiroConfiguration.shirFilter()");
        
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //shiroFilterFactoryBean.setLoginUrl("/view/login.html");
        //shiroFilterFactoryBean.setSuccessUrl("/view/index.html");
        //shiroFilterFactoryBean.setUnauthorizedUrl("/view/error/403.html");
        
        //定义过滤器
        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        filters.put("fLogin", new ForceLogoutFilter());
        filters.put("mauthc", new WebAndAppFormAuthenticationFilter());
        filters.put("mperms", new FramePermissionsAuthorizationFilter(shiroProperties.isAuthz()));
        
        shiroFilterFactoryBean.setFilters(filters);
        
        //拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//        //静态文件
//        filterChainDefinitionMap.put("/assets/**", "anon");
//        filterChainDefinitionMap.put("/css/**", "anon");
//        filterChainDefinitionMap.put("/fonts/**", "anon");
//        filterChainDefinitionMap.put("/img/**", "anon");
//        filterChainDefinitionMap.put("/js/**", "anon");
//        filterChainDefinitionMap.put("/sampledata/**", "anon");
//        
//        //view页面
//        filterChainDefinitionMap.put("/view/login", "anon");
//        filterChainDefinitionMap.put("/view/login.html", "anon");
//        
//        //controller 访问
//        filterChainDefinitionMap.put("/access/**", "anon");
//        filterChainDefinitionMap.put("/logout", "logout");
//        
//        filterChainDefinitionMap.put("/**", "fLogin, mauthc, mperms");
        //通过加载properties文件实现
        OrderedPropertiesReader opReader = OrderedPropertiesReader.getInstance(); 
        Map<String, String> propertyMap = opReader.getPropertyMap("shiroFilterChainDefinition.properties");
        filterChainDefinitionMap.putAll(propertyMap);
        
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    
    /**
     * 会话Cookie模板
     */
    @Bean("sessionIdCookie")
    @ConfigurationProperties("spring.shiro.session-id-cookie")
    public Cookie sessionIdCookie(){
        return new SimpleCookie("yui2-token");//默认为yui2-token
    }
    
    /**
     * 会话ID生成器
     */
    @Bean("sessionIdGenerator")
    public SessionIdGenerator sessionIdGenerator(){
        return new JavaUuidSessionIdGenerator();
    }
    
    /**
     * Redis 实现 ShiroSessionDao
     */
    @Bean("sessionDAO")
    @ConfigurationProperties("spring.shiro.session-dao")
    public SessionDAO sessionDAO(RedisRepository redisRepository, SessionIdGenerator sessionIdGenerator){
        RedisShiroSessionDao redisShiroSessionDao = new RedisShiroSessionDao();
        redisShiroSessionDao.setRedisRepository(redisRepository);
        redisShiroSessionDao.setSessionIdGenerator(sessionIdGenerator);
        return redisShiroSessionDao;
    }
    
    @Bean("cacheManager")
    public CacheManager cacheManager(RedisRepository redisRepository){
        ShiroRedisCacheManager cacheManager = new ShiroRedisCacheManager();
        cacheManager.setRedisRepository(redisRepository);
        return cacheManager;
    }
    
    @Bean("sessionManager")
    @ConfigurationProperties("spring.shiro.session-mgr")
    public SessionManager sessionManager(SessionDAO sessionDAO, Cookie sessionIdCookie){
        WebAndAppSessionManager sessionManager = new WebAndAppSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setSessionDAO(sessionDAO);
        sessionManager.setSessionIdCookie(sessionIdCookie);
        return sessionManager;
    }
    
    @Bean("subjectFactory")
    public SubjectFactory subjectFactory(){
        return new DefaultWebSubjectFactory();
    }
    
    @Bean("securityManager")
    public SecurityManager securityManager(SessionManager sessionManager, Realm shiroDBRealm, 
            SubjectFactory subjectFactory, CacheManager cacheManager){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(sessionManager);
        securityManager.setRealm(shiroDBRealm);
        securityManager.setSubjectFactory(subjectFactory);
        securityManager.setCacheManager(cacheManager);
        return securityManager;
    }
    
    @Bean("shiroDBRealm")
    @ConfigurationProperties("spring.shiro.shiro-db-realm")
    public Realm shiroDBRealm(){
        ShiroDBRealm shiroDBRealm = new ShiroDBRealm();
        shiroDBRealm.setAuthenticationCacheName("authenticationCache");
        shiroDBRealm.setAuthorizationCacheName("authorizationCache");
        return shiroDBRealm;
    }
    
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    
    @Bean("methodInvokingFactoryBean")
    public MethodInvokingFactoryBean methodInvokingFactoryBean(SecurityManager securityManager) {
        MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
        factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        factoryBean.setArguments(new Object[]{securityManager});
        return factoryBean;
    }
    
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
    
  

    
}

Shiro 用户信息和授权信息,缓存配置,这里使用了redis缓存

import java.util.Collection;
import java.util.Set;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import yui.comn.redis.RedisRepository;
import yui.comn.utils.SerializeUtils;

/**
 * Redis 实现Shiro缓存
 * @author yuyi
 */
public class ShiroRedisCache<K, V> implements Cache<K, V> {
    public static Logger logger = LoggerFactory.getLogger(ShiroRedisCache.class);
    
    private String  name;
    private RedisRepository redisRepository;
    
    public ShiroRedisCache(String name, RedisRepository redisRepository) {
        this.name = name;
        this.redisRepository = redisRepository;
    }
    
    private byte[] getByteKey(K key) {
        if (key instanceof String) {
            String preKey = key.toString();
            return preKey.getBytes();
        } else {
            return SerializeUtils.serialize(key);
        }
    }
    
    private byte[] getByteName() {
        return name.getBytes();
    }
    
    @Override
    public void clear() throws CacheException {
        logger.debug("从redis中删除所有元素");
        try {
            redisRepository.del(getByteName());
        } catch (Throwable t) {
            throw new CacheException(t);
        }
        
    }

    @SuppressWarnings("unchecked")
    @Override
    public V get(K key) throws CacheException {
        logger.debug("根据key从Redis中获取对象 key [" + key + "]");
        try {
            if (key == null) {
                return null;
            } else {
                V value = (V) redisRepository.hGet(getByteName(), getByteKey(key));
                return value;
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public Set<K> keys() {
        try {
            Set<K> keys = redisRepository.hKeys(getByteName());
            return keys;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @Override
    public V put(K key, V val) throws CacheException {
        logger.debug("根据key从存储 key [" + key + "]");
        try {
            redisRepository.hPut(getByteName(), getByteKey(key), SerializeUtils.serialize(val));
            return val;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @Override
    public V remove(K key) throws CacheException {
        logger.debug("从redis中删除 key [" + key + "]");
        try {
            V previous = get(key);
            redisRepository.hDel(getByteName(), getByteKey(key));
            return previous;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @Override
    public int size() {
        try {
            Long longSize = new Long(redisRepository.hLen(getByteName()));
            return longSize.intValue();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public Collection<V> values() {
        try {
            Collection<V> values = redisRepository.hVals(getByteName());
            return values;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    
}
import org.apache.shiro.cache.AbstractCacheManager;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;

import yui.comn.redis.RedisRepository;

/**
 * redis 缓存管理器
 * @author yuyi
 */
public class ShiroRedisCacheManager extends AbstractCacheManager {

    private RedisRepository redisRepository;
    
    @Override
    protected Cache<String, Object> createCache(String cacheName) throws CacheException {
        return new ShiroRedisCache<String, Object>(cacheName, redisRepository);
    }

    public RedisRepository getRedisRepository() {
        return redisRepository;
    }

    public void setRedisRepository(RedisRepository redisRepository) {
        this.redisRepository = redisRepository;
    }
}

Shiro 基于redis存储的SessionDao

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import yui.comn.redis.RedisRepository;
import yui.comn.utils.DateUtils;

/**
 * Redis实现的 ShiroSessionDao
 * @author yuyi
 */
public class RedisShiroSessionDao extends AbstractSessionDAO {
    public Logger logger = LoggerFactory.getLogger(RedisShiroSessionDao.class);
    
    private String sessionPrefix = "yui2-sid";
    private Long expire = 28800L; //28800L;
    private RedisRepository redisRepository;
    
    @Override
    public void update(Session session) throws UnknownSessionException {
        try {
            SimpleSession simpleSession = (SimpleSession) session;
            simpleSession.setStartTimestamp(DateUtils.currentTimestamp());
            
            long expireSec = null != expire ? expire : session.getTimeout()/1000;
            redisRepository.put(getTbSid(session), session, expireSec);
        } catch (Exception e) {
            logger.error("更新session失败", e);
        }

    }

    @Override
    public void delete(Session session) {
        try {
            redisRepository.del(getTbSid(session));
        } catch (Exception e) {
            logger.error("删除session失败", e);
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public Collection<Session> getActiveSessions() {
        String pattern = getTbSid(sessionPrefix + "-*");
        List list = null;
        try {
            Set keys = redisRepository.keys(pattern);
            if (null != keys) {
                list =  new ArrayList<Object>(keys);
            }
        } catch (Exception e) {
            logger.error("获取有效session失败", e);
        }
        return list;
    }

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = session.getId();
        try {
            super.assignSessionId(session, sessionPrefix + "-" + super.generateSessionId(session));
            update(session);
            sessionId = session.getId();
        } catch (Exception e) {
            logger.error("创建session失败", e);
        }
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        Session session = null;
        try {
            session = (Session) redisRepository.get(getTbSid(sessionId));
        } catch (Exception e) {
            logger.error("读取session失败", e);
        }
        return session;
    }

    public String getTbSid(Session session) {
        if (null == session.getId()) {
            return null;
        }
        return getTbSid(session.getId());
    }
    
    public String getTbSid(String sid) {
        return new StringBuffer(sessionPrefix).append(":").append(sid).toString();
    }
    
    public String getTbSid(Serializable sid) {
        return getTbSid(sid.toString());
    }
    
    
    
    
    public void setSessionPrefix(String sessionPrefix) {
        this.sessionPrefix = sessionPrefix;
    }

    public void setExpire(Long expire) {
        this.expire = expire;
    }

    public void setRedisRepository(RedisRepository redisRepository) {
        this.redisRepository = redisRepository;
    }
}

Shiro  自己覆写实现类,WebAndAppSessionManager继承DefaultWebSessionManager,如果header带有token信息,先从header中获取token信息。

如果header没有带有token信息,再去shiro默认实现中去获取。

Shiro默认先去cookie中去获取,如果cookie中没有token,再去参数中去获取是否带有token参数信息。

import java.io.Serializable;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;

/**
 * @author yuyi
 */
public class WebAndAppSessionManager extends DefaultWebSessionManager {

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        Cookie sessionIdCookie = getSessionIdCookie();
        HttpServletRequest httpReq = (HttpServletRequest) request;
        String shiroToken = httpReq.getHeader(sessionIdCookie.getName());
        if (null != shiroToken) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
                    ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
            return shiroToken;
        }
        return super.getSessionId(request, response);
    }   
    
}

Shiro 实现ShiroDBRealm类,实现认证和授权方法,和清除缓存方法。

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;

import yui.comn.shiro.mgr.IUserDetailMgrx;
import yui.comn.shiro.utils.ShiroConstant;
import yui.comn.utils.SpringContextUtils;
import yui.comn.utils.UserInfo;

/**
 * @author yuyi
 */
//@Service
public class ShiroDBRealm extends AuthorizingRealm {

    private IUserDetailMgrx userDetailMgrx;
    
    private IUserDetailMgrx getUserDetailMgrx() {
        //解决shiro不能注入dubbo的bug
        if (null == userDetailMgrx) {
            userDetailMgrx = (IUserDetailMgrx) SpringContextUtils.getBean(IUserDetailMgrx.USER_DETAIL_MGRX);
        }
        return userDetailMgrx;
    }
    
    public void setUserDetailMgrx(IUserDetailMgrx userDetailMgrx) {
        this.userDetailMgrx = userDetailMgrx;
    }
    
    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        
        String principal = token.getPrincipal().toString();
        
        //forceLogoutBefore(principal);
        
        UserInfo userInfo = getUserDetailMgrx().getUserInfo(principal);
        
        PrincipalCollection principalCollection = new SimplePrincipalCollection(userInfo, ShiroConstant.AUTHORIZING_REALM_NAME);
        return new SimpleAuthenticationInfo(principalCollection, token.getCredentials());
    }
    
    /**
     * 强制退出之前的账号
     * @param username
     */
    @SuppressWarnings("unused")
    private void forceLogoutBefore(String username) {
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
        Collection<Session> activeSessions = sessionManager.getSessionDAO().getActiveSessions();
        for (Session session : activeSessions) {
            SimplePrincipalCollection principals = (SimplePrincipalCollection) session.getAttribute(
                    DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
            if (StringUtils.equalsIgnoreCase(userInfo.getUsername(), username)) {
                if (null == session.getAttribute(ShiroConstant.SESSION_FORCE_LOGOUT_KEY)) {
                    session.setAttribute(ShiroConstant.SESSION_FORCE_LOGOUT_KEY, Boolean.TRUE); 
                    sessionManager.getSessionDAO().update(session);
                }
            }
        }
    }
    
    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();  //从这里可以从cas server获得认证通过的用户名,得到后我们可以根据用户名进行具体的授权
        
        Map<String, Set<String>> roleAndPermMap = getUserDetailMgrx().getRoleAndPerm(userInfo.getSysUserPk());
        
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
        authorizationInfo.setRoles(roleAndPermMap.get(IUserDetailMgrx.ROLE_MAP_KEY));
        authorizationInfo.setStringPermissions(roleAndPermMap.get(IUserDetailMgrx.PERM_MAP_KEY));
        
        return authorizationInfo;
    }
    
    @Override
    public AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
        return super.getAuthorizationInfo(principals);
    }
    
    /**
     * 覆写认证缓存key
     */
    @Override
    protected Object getAuthenticationCacheKey(PrincipalCollection principals) {
        UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
        return userInfo.getUsername();
        //return super.getAuthenticationCacheKey(principals);
    }
    
    /**
     * 覆写授权缓存key
     */
    @Override
    protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
        UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
        return userInfo.getUsername();
        //return super.getAuthenticationCacheKey(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }
    
    /**
     * 通过用户名清除缓存
     */
    public void clearCache(String username) {
        PrincipalCollection principals = new SimplePrincipalCollection(
                new UserInfo(username), ShiroConstant.AUTHORIZING_REALM_NAME);
        clearCache(principals);
    }
    
    /**
     * 清除认证和授权缓存
     */
    public void clearAllCache() {
        clearAllAuthcCache();
        clearAllAuthzCache();
    }
    
    /**
     * 清除认证缓存
     */
    public void clearAllAuthcCache() {
        getAuthenticationCache().clear();
    }
    
    /**
     * 清除授权缓存
     */
    public void clearAllAuthzCache() {
        getAuthorizationCache().clear();
    }
    
    
}
/**
 * @author yuyi
 */
public class ShiroConstant {

    //realm名称
    public static final String AUTHORIZING_REALM_NAME="shiroDbAuthorizingRealmName";
    //强制退出标识
    public static final String SESSION_FORCE_LOGOUT_KEY = "sessionForceLogoutKey";
    
}

Shiro  自己覆写实现类,自定义过滤去,WebAndAppFormAuthenticationFilter继承FormAuthenticationFilter,实现异步返回json数据结果,对于前后端分离,全部请求都返回json。

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Web And App FormAuthenticationFilter
 * @author yuyi
 */
public class WebAndAppFormAuthenticationFilter extends FormAuthenticationFilter {

private static final Logger logger = LoggerFactory.getLogger(WebAndAppFormAuthenticationFilter.class);
    
    private String noAuthenticationReturn = "{\"status\": \"401\", \"message\":\"未授权的访问,请检查Token后重试\"}";
    
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
            throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Login submission detected.  Attempting to execute login.");
                }
                return executeLogin(request, response);
            } else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Login page view.");
                }
                //allow them to see the login page ;)
                return true;
            }
        } else {
            if (logger.isTraceEnabled()) {
                logger.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
            }
            response.setContentType("text/html;charset=UTF-8");
            
          //如果header带有token信息
            String refSessionIdSource = (String) request.getAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
            if (ShiroHttpServletRequest.URL_SESSION_ID_SOURCE.equals(refSessionIdSource)) {
                response.getWriter().write(noAuthenticationReturn);
            } else {
                //如果ajax异步请求
                HttpServletRequest httpRequest = (HttpServletRequest) request;
                String reqtType = httpRequest.getHeader("X-Requested-With");
                if (null != reqtType) {
                    response.getWriter().write(noAuthenticationReturn);
                } else {
                    //因为都是异步请求
                    response.getWriter().write(noAuthenticationReturn);
                    //saveRequestAndRedirectToLogin(request, response);
                }
            }
            return false;
        }
    }
    
    public void setNoAuthenticationReturn(String noAuthenticationReturn) {
        this.noAuthenticationReturn = noAuthenticationReturn;
    }
}

Shiro  自己覆写实现类,自定义过滤去,ForceLogoutFilter继承AccessControlFilter,如果有用户被强制退出,该用户再次访问时会返回"{\"status\": \"402\", \"msg\":\"被强制退出\"}",表示用户被强制退出,去要再次登陆。

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.session.Session;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import yui.comn.shiro.utils.ShiroConstant;

/**
 * 强制退出
 * @author yuyi
 */
public class ForceLogoutFilter extends AccessControlFilter {

private static final Logger logger = LoggerFactory.getLogger(ForceLogoutFilter.class);
    @SuppressWarnings("unused")
    private String redirectUrl; //= "http://127.0.0.1:8080/cas/logout?service=http://127.0.0.1:8181/adm/";
    private String forceLogoutReturn = "{\"status\": \"402\", \"msg\":\"被强制退出\"}";
    
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response,
            Object mappedValue) throws Exception {
        
        Session session = getSubject(request, response).getSession(false);  
        if(null == session) { 
            return true;  
        }
        return null == session.getAttribute(ShiroConstant.SESSION_FORCE_LOGOUT_KEY);  
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
            throws Exception {
        if (logger.isTraceEnabled()) {
            logger.trace("Attempting to access a path which force logout.  Forwarding to the " +
                    "Authentication url [" + getLoginUrl() + "]");
        }
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        String refSessionIdSource = (String) request.getAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
        if (ShiroHttpServletRequest.URL_SESSION_ID_SOURCE.equals(refSessionIdSource)) {
            logout(request, response);
            response.getWriter().write(forceLogoutReturn);
        } else {
            String reqtType = httpRequest.getHeader("X-Requested-With");
            if (null != reqtType) {
                response.getWriter().write(forceLogoutReturn);
            } else {
                logout(request, response);
                saveRequest(request);
                //WebUtils.issueRedirect(request, response, redirectUrl);
                saveRequestAndRedirectToLogin(request, response);
            }
        }
        return false;  
    }
    
    //删除session
    private void logout(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        //Exception exception = null;
        try {  
            getSubject(request, response).logout();//强制退出  
        } catch (Exception e) {
            logger.error("强制退出失败", e);
        } finally {
            //cleanup(request, response, exception);
        }
    }

    public void setRedirectUrl(String redirectUrl) {
        this.redirectUrl = redirectUrl;
    }
    public void setForceLogoutReturn(String forceLogoutReturn) {
        this.forceLogoutReturn = forceLogoutReturn;
    }

}

Shiro  自己覆写实现类,自定义过滤去,FramePermissionsAuthorizationFilter继承PermissionsAuthorizationFilter,进行授权过滤,默认不开启,如果开启会对每个接口进行授权。

import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author yuyi
 */
public class FramePermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
    private static Logger logger = LoggerFactory.getLogger(FramePermissionsAuthorizationFilter.class);
    
    private boolean isAuthz;
    
    public FramePermissionsAuthorizationFilter() {
        super();
    }
    
    public FramePermissionsAuthorizationFilter(boolean isAuthz) {
        super();
        this.isAuthz = isAuthz;
    }

    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        
        if (!isAuthz) {
            return true;
        }
        
        HttpServletRequest req = (HttpServletRequest) request;
        Subject subject = getSubject(request, response);
        
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        
        int index = uri.indexOf(contextPath);
        if(index > -1){
            uri = uri.substring(index + contextPath.length());
        }
        
        //首页免登录
        if (StringUtils.equals("/", uri)) {
            return true;
        }
        //阿里druid数据源默认全部允许
        if (StringUtils.indexOf(uri, "/druid") == 0) {
            return true;
        }
        //如果不是菜单,变成shiro资源格式
        if (!StringUtils.contains(uri, ".")) {
            uri = StringUtils.substring(uri, 1);
            uri = StringUtils.replace(uri, "/", ":");
        }
        
        boolean permitted = subject.isPermitted(uri);
        
        if (!permitted) {
            logger.warn("uri[" + uri + "] is no perm");
        }
        
        return permitted;
    }

    public boolean isAuthz() {
        return isAuthz;
    }

    public void setAuthz(boolean isAuthz) {
        this.isAuthz = isAuthz;
    }
}

猜你喜欢

转载自blog.csdn.net/ssyujay/article/details/81456132