package com.jy.shiro.config; import com.jy.domain.admin.User; import com.jy.service.PermissionService; import com.jy.service.SysUserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import javax.annotation.Resource; import java.util.HashSet; import java.util.Set; public class MyShiroRealm extends AuthorizingRealm { @Resource private SysUserService sysUserService; @Resource private PermissionService permissionService; public MyShiroRealm(CacheManager cacheManager, CredentialsMatcher matcher) { super(cacheManager, matcher); } public MyShiroRealm() { } /** * 认证信息.(身份验证) : Authentication 是用来验证用户身份 * @param authcToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; String username = (String) token.getPrincipal(); User user = sysUserService.getUser(username); if (user == null) { /*没找到帐号*/ throw new UnknownAccountException(); } //帐号锁定 if (Boolean.TRUE.equals(user.getLocked())) { throw new LockedAccountException(); } //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user.getUsername(), user.getPassword(),ByteSource.Util.bytes(user.getSalt()), getName() ); return authenticationInfo; } /** * 授权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //获取的Principal是username是因为在认证的时候存放的信息就是user.getUsername(); /* SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user.getUsername(), user.getPassword(),ByteSource.Util.bytes(user.getSalt()), getName() ); */ String username = (String)SecurityUtils.getSubject().getPrincipal(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //设置角色 Set<String> roleSet=new HashSet<String>(); roleSet.add("admin"); info.setRoles(roleSet); //设置对应的权限 Set<String> permissionSet=permissionService.getUserPermission(username); info.setStringPermissions(permissionSet); return info; } }
2.springboot 结合shiro的配置类
package com.jy.shiro.config; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import java.util.LinkedHashMap; import java.util.Map; /** * @author 作者 z77z * @date 创建时间:2017年2月10日 下午1:16:38 */ @Configuration public class ShiroConfig { //配置自定义的密码比较器 @Bean(name = "hashedCredentialsMatcher") public HashedCredentialsMatcher getHashedCredentialsMatcher() { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("MD5"); credentialsMatcher.setHashIterations(2); credentialsMatcher.setStoredCredentialsHexEncoded(true); return credentialsMatcher; } //配置自定义的权限登录器 @Bean(name="authRealm") public MyShiroRealm authRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) { MyShiroRealm authRealm=new MyShiroRealm(); authRealm.setCredentialsMatcher(matcher); return authRealm; } //配置核心安全事务管理器 @Bean(name="securityManager") public DefaultWebSecurityManager securityManager(@Qualifier("authRealm") MyShiroRealm authRealm) { System.err.println("--------------shiro已经加载----------------"); DefaultWebSecurityManager manager=new DefaultWebSecurityManager(); manager.setRealm(authRealm); return manager; } @Bean(name="shiroFilterFactoryBean") public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); filterChainDefinitionMap.put("/favicon.ico", "anon"); filterChainDefinitionMap.put("/assets/**", "anon"); filterChainDefinitionMap.put("/logout", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/error", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setLoginUrl("login"); shiroFilterFactoryBean.setSuccessUrl("index"); shiroFilterFactoryBean.setUnauthorizedUrl("error"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager manager) { AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(manager); return advisor; } }
package com.jy.shiro.filter; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class CustomFormAuthenticationFilter extends FormAuthenticationFilter { protected Logger logger = LoggerFactory.getLogger(CustomFormAuthenticationFilter.class); @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { this.issueSuccessRedirect(request, response); return false; } }
package com.jy.shiro.filter; import com.alibaba.fastjson.JSON; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.DefaultSessionKey; import org.apache.shiro.session.mgt.SessionManager; 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 java.io.IOException; import java.io.PrintWriter; import java.io.Serializable; import java.util.Deque; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; /** * 1.读取当前登录用户名,获取在缓存中的sessionId队列 * 2.判断队列的长度,大于最大登录限制的时候,按踢出规则 * 将之前的sessionId中的session域中存入kickout:true,并更新队列缓存 * 3.判断当前登录的session域中的kickout如果为true, * 想将其做退出登录处理,然后再重定向到踢出登录提示页面 */ public class KickoutSessionControlFilter extends AccessControlFilter { private String kickoutUrl; //踢出后到的地址 private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户 private int maxSession = 1; //同一个帐号最大会话数 默认1 private SessionManager sessionManager; private Cache<String, Deque<Serializable>> cache; public void setKickoutUrl(String kickoutUrl) { this.kickoutUrl = kickoutUrl; } public void setKickoutAfter(boolean kickoutAfter) { this.kickoutAfter = kickoutAfter; } public void setMaxSession(int maxSession) { this.maxSession = maxSession; } public void setSessionManager(SessionManager sessionManager) { this.sessionManager = sessionManager; } //设置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) throws Exception { Subject subject = getSubject(request, response); if (!subject.isAuthenticated() && !subject.isRemembered()) { //如果没有登录,直接进行之后的流程 return true; } Session session = subject.getSession(); String username = (String) subject.getPrincipal(); Serializable sessionId = session.getId(); //读取缓存 没有就存入 Deque<Serializable> deque = cache.get(username); //如果此用户没有session队列,也就是还没有登录过,缓存中没有 //就new一个空队列,不然deque对象为空,会报空指针 if (deque == null) { deque = new LinkedList<Serializable>(); } //如果队列里没有此sessionId,且用户没有被踢出;放入队列 if (!deque.contains(sessionId) && session.getAttribute("kickout") == null) { //将sessionId存入队列 deque.push(sessionId); //将用户的sessionId队列缓存 cache.put(username, deque); } //如果队列里的sessionId数超出最大会话数,开始踢人 while (deque.size() > maxSession) { Serializable kickoutSessionId = null; if (kickoutAfter) { //如果踢出后者 kickoutSessionId = deque.removeFirst(); //踢出后再更新下缓存队列 cache.put(username, deque); } else { //否则踢出前者 kickoutSessionId = deque.removeLast(); //踢出后再更新下缓存队列 cache.put(username, deque); } try { //获取被踢出的sessionId的session对象 Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId)); if (kickoutSession != null) { //设置会话的kickout属性表示踢出了 kickoutSession.setAttribute("kickout", true); } } catch (Exception e) {//ignore exception } } //如果被踢出了,直接退出,重定向到踢出后的地址 if ((Boolean) session.getAttribute("kickout") != null && (Boolean) session.getAttribute("kickout") == true) { //会话被踢出了 try { //退出登录 subject.logout(); } catch (Exception e) { //ignore } saveRequest(request); Map<String, String> resultMap = new HashMap<String, String>(); //判断是不是Ajax请求 if ("XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"))) { resultMap.put("user_status", "300"); resultMap.put("message", "您已经在其他地方登录,请重新登录!"); //输出json串 out(response, resultMap); } else { //重定向 WebUtils.issueRedirect(request, response, kickoutUrl); } return false; } return true; } private void out(ServletResponse hresponse, Map<String, String> resultMap) throws IOException { try { hresponse.setCharacterEncoding("UTF-8"); PrintWriter out = hresponse.getWriter(); out.println(JSON.toJSONString(resultMap)); out.flush(); out.close(); } catch (Exception e) { System.err.println("KickoutSessionFilter.class 输出JSON异常,可以忽略。"); } } }