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的配置類java
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異常,能夠忽略。"); } } }