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; }