springboot整合shiro、ehcache
阿新 • • 發佈:2018-12-03
只需要一個自定義realm、一個shiro配置類和ehcache
自定義realm:
package com.example.demo.config; import com.example.demo.entity.RoleEntity; import com.example.demo.entity.UserEntity; import com.example.demo.jpa.UserJpa; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.HostUnauthorizedException; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; /** * @author FastKing * @version 1.0 * @date 2018/9/19 11:54 **/ public class MyShiroRealm extends AuthorizingRealm { @Autowired private UserJpa userJpa; /** * 許可權認證 * * @param principalCollection * @author FastKing * @date 10:11 2018/9/27 **/ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) throws AuthorizationException { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); UserEntity userEntity = (UserEntity) principalCollection.getPrimaryPrincipal(); Set<RoleEntity> roleEntitySet = userEntity.getRoleEntitySet(); Set<String> permissionNameSet = new HashSet<>(); simpleAuthorizationInfo.setRoles(roleEntitySet.stream().map(RoleEntity::getName).collect(Collectors.toSet())); roleEntitySet.forEach(roleEntity -> roleEntity.getPermissionEntitySet().forEach(permissionEntity -> permissionNameSet.add(permissionEntity.getName()))); simpleAuthorizationInfo.setStringPermissions(permissionNameSet); if (permissionNameSet.size() <= 0) { throw new HostUnauthorizedException("沒有許可權"); } return simpleAuthorizationInfo; } /** * 身份認證 * * @param authenticationToken * @author FastKing * @date 16:48 2018/9/27 **/ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken; String username = usernamePasswordToken.getUsername(); UserEntity userEntity = userJpa.findByUsername(username); if (null == userEntity) { throw new UnknownAccountException("帳號不存在"); } String password = String.valueOf(usernamePasswordToken.getPassword()); SimpleHash md5 = new SimpleHash("MD5", password, ByteSource.Util.bytes(userEntity.getSalt()), 1024); if (!StringUtils.equals(userEntity.getPassword(), md5.toString())) { throw new IncorrectCredentialsException("密碼錯誤"); } if (userEntity.getIsLocked() == 1) { throw new LockedAccountException("帳號已鎖定"); } if (userEntity.getIsForbidden() == 1) { throw new DisabledAccountException("帳號已禁用"); } return new SimpleAuthenticationInfo(username, md5, ByteSource.Util.bytes(userEntity.getSalt()), getName()); } }
自定義realm不難理解,主要是對賬號進行認證和授權
認證方法中的密碼使用了MD5和鹽值加密
授權方法的作用是把許可權寫入cookie,一般使用jsp中的shiro標籤庫時會重寫授權方法
下面是shiro配置類:
package com.example.demo.config; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.codec.Base64; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.SessionListener; import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler; import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.LinkedList; /** * @author FastKing * @version 1.0 * @date 2018/9/19 11:02 **/ @Configuration public class ShiroConfig { /** * @param securityManager * @author FastKing * @date 17:00 2018/9/27 **/ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //配置安全管理器,shiro核心,負責與其他安全元件的互動,並管理Subject shiroFilterFactoryBean.setSecurityManager(securityManager); //配置登入頁面 shiroFilterFactoryBean.setLoginUrl("/view/login.html"); //配置登入成功頁面 shiroFilterFactoryBean.setSuccessUrl("/view/user/index.html"); //配置未授權頁面 shiroFilterFactoryBean.setUnauthorizedUrl("/view/error/403.html"); //配置攔截器 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/view/**", "user"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 加密器 * * @author FastKing * @date 16:59 2018/9/27 **/ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); //雜湊演算法 hashedCredentialsMatcher.setHashAlgorithmName("MD5"); //雜湊次數 hashedCredentialsMatcher.setHashIterations(1024); //是否儲存雜湊後的密碼為16進位制 hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); return hashedCredentialsMatcher; } /** * 安全管理器 * * @author FastKing * @date 17:23 2018/9/27 **/ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(myShiroRealm()); ///設定session管理器 defaultWebSecurityManager.setSessionManager(getDefaultWebSessionManager()); //設定記住我管理器 defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager()); //設定快取管理器 defaultWebSecurityManager.setCacheManager(ehCacheManager()); return defaultWebSecurityManager; } /** * 自定義realm * * @author FastKing * @date 12:38 2018/9/28 **/ @Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } /** * session管理器 * * @author FastKing * @date 13:06 2018/9/28 **/ private DefaultWebSessionManager getDefaultWebSessionManager() { DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager(); //設定過期時間30分鐘 defaultWebSessionManager.setGlobalSessionTimeout(1800000); //session定期驗證 defaultWebSessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler()); defaultWebSessionManager.setDeleteInvalidSessions(true); //session cookie defaultWebSessionManager.setSessionIdCookie(getSessionIdCookie()); defaultWebSessionManager.setSessionIdCookieEnabled(true); defaultWebSessionManager.setSessionValidationSchedulerEnabled(true); //session監聽 LinkedList<SessionListener> list = new LinkedList<>(); list.add(new MyShiroSessionListener()); defaultWebSessionManager.setSessionListeners(list); //session的儲存 EnterpriseCacheSessionDAO cacheSessionDAO = new EnterpriseCacheSessionDAO(); defaultWebSessionManager.setCacheManager(ehCacheManager()); defaultWebSessionManager.setSessionDAO(cacheSessionDAO); return defaultWebSessionManager; } /** * ehcache快取管理器 * * @author FastKing * @date 12:39 2018/9/28 **/ @Bean public EhCacheManager ehCacheManager() { EhCacheManager ehCacheManager = new EhCacheManager(); ehCacheManager.setCacheManagerConfigFile("classpath:shiro-ehcache.xml"); return ehCacheManager; } /** * rememberMe cookie物件 * * @author FastKing * @date 12:49 2018/9/28 **/ private SimpleCookie rememberMeCookie() { SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); //防止cookie暴露給客戶端 simpleCookie.setHttpOnly(true); //設定過期時間30天 simpleCookie.setMaxAge(2592000); return simpleCookie; } private SimpleCookie getSessionIdCookie() { SimpleCookie simpleCookie = new SimpleCookie("sid"); simpleCookie.setHttpOnly(true); simpleCookie.setMaxAge(-1); return simpleCookie; } /** * 記住我管理器 * * @author FastKing * @date 12:52 2018/9/28 **/ private CookieRememberMeManager cookieRememberMeManager() { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag==")); return cookieRememberMeManager; } /** * session驗證器 * * @author FastKing * @date 13:18 2018/9/28 **/ private ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() { ExecutorServiceSessionValidationScheduler executorServiceSessionValidationScheduler = new ExecutorServiceSessionValidationScheduler(); //15分鐘校驗一次 executorServiceSessionValidationScheduler.setInterval(900000); return executorServiceSessionValidationScheduler; } }
配置類中都寫了註釋,也很容易理解
最後時ehcache.xml,如果你不是用ehcache管理快取,可以忽略:
<?xml version="1.0" encoding="UTF-8"?> <ehcache updateCheck="false" dynamicConfig="false"> <diskStore path="java.io.tmpdir"/> <cache name="users" timeToLiveSeconds="300" maxEntriesLocalHeap="1000"/> <!-- name:快取名稱。 maxElementsInMemory:快取最大個數。 eternal:物件是否永久有效,一但設定了,timeout將不起作用。 timeToIdleSeconds:設定物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。 timeToLiveSeconds:設定物件在失效前允許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false物件不是永久有效時使用,預設是0.,也就是物件存活時間無窮大。 overflowToDisk:當記憶體中物件數量達到maxElementsInMemory時,Ehcache將會物件寫到磁碟中。 diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。 maxElementsOnDisk:硬碟最大快取個數。 diskPersistent:是否快取虛擬機器重啟期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。 memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。 clearOnFlush:記憶體數量最大時是否清除。 --> <defaultCache name="defaultCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" maxElementsOnDisk="100000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> </ehcache>
寫個controller測試一下:
package com.example.demo.controller;
import com.example.demo.jpa.UserJpa;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName HelloController
* @Description TODO
* @Author FastKing
* @Date 2018/9/13 9:02
* @Version 1.0
**/
@RestController
public class HelloController {
@Autowired
private UserJpa userJpa;
@ResponseBody
@RequestMapping(value = "/login/{username}/{password}/{rememberMe}", method = RequestMethod.GET)
public Object login(@PathVariable String username, @PathVariable String password, @PathVariable String rememberMe) {
try {
SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password, Boolean.parseBoolean(rememberMe)));
} catch (Exception e) {
return e.getMessage();
}
return null;
}
@RequestMapping(value = "/list")
public Object list() {
return userJpa.findAll();
}
}
如果你要使用“記住我”功能,把引數傳到UsernamePasswordToken就可以