一、Shiro
Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。使用Shiro的易於理解的API,您可以快速、輕鬆地獲得任何應用程序,從最小的移動應用程序到最大的網絡和企業應用程序。
主要功能
三個核心組件:Subject, SecurityManager 和 Realms.
Subject:即“當前操作用戶”。但是,在Shiro中,Subject這一概念並不僅僅指人,也可以是第三方進程、後臺帳戶(Daemon Account)或其他類似事物。它僅僅意味着“當前跟軟件交互的東西”。
Subject代表了當前用戶的安全操作,SecurityManager則管理所有用戶的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通過SecurityManager來管理內部組件實例,並通過它來提供安全管理的各種服務。
Realm: Realm充當了Shiro與應用安全數據間的“橋樑”或者“連接器”。也就是說,當對用戶執行認證(登錄)和授權(訪問控制)驗證時,Shiro會從應用配置的Realm中查找用戶及其權限信息。
從這個意義上講,Realm實質上是一個安全相關的DAO:它封裝了數據源的連接細節,並在需要時將相關數據提供給Shiro。當配置Shiro時,你必須至少指定一個Realm,用於認證和(或)授權。配置多個Realm是可以的,但是至少需要一個。
Shiro內置了可以連接大量安全數據源(又名目錄)的Realm,如LDAP、關係數據庫(JDBC)、類似INI的文本配置資源以及屬性文件等。如果缺省的Realm不能滿足需求,你還可以插入代表自定義數據源的自己的Realm實現。
二、Maven包引入
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
三、註解配置
package com.zxhy.shiro.config;
import com.zxhy.shiro.filter.RememberAuthenticationFilter;
import com.zxhy.shiro.realm.MyRealm;
import com.zxhy.shiro.redisSession.CachingShiroSessionDao;
import com.zxhy.shiro.redisSession.SessionManager;
import com.zxhy.shiro.redisSession.ShiroSessionListener;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class ShiroConfig {
//權限過濾器的工廠類
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//攔截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不會被攔截的鏈接 順序判斷
filterChainDefinitionMap.put("/app/**", "anon");
filterChainDefinitionMap.put("/service/**", "anon");
filterChainDefinitionMap.put("/statics/**", "anon");
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/index.html", "anon");
filterChainDefinitionMap.put("/login/login", "anon");
filterChainDefinitionMap.put("/login/captcha", "anon");
filterChainDefinitionMap.put("/swagger/**", "anon");
filterChainDefinitionMap.put("/**", "rememberAuthFilter");
Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
filters.put("rememberAuthFilter",new RememberAuthenticationFilter());
shiroFilterFactoryBean.setLoginUrl("/index.html");
// 登錄成功後要跳轉的鏈接
shiroFilterFactoryBean.setSuccessUrl("/index1.html");
//未授權界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/");
shiroFilterFactoryBean.setFilters(filters);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
//設置Realms以及session管理器
@Bean
public SecurityManager securityManager(SessionManager sessionManager, MyRealm myRealm ) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
securityManager.setSessionManager(sessionManager);
return securityManager;
}
//配置session管理器
@Bean
public SessionManager sessionManager(SimpleCookie sessionIdCookie,CachingShiroSessionDao sessionDao,SessionListener sessionListener) {
SessionManager sessionManager = new SessionManager();
sessionManager.setDeleteInvalidSessions(false);
sessionManager.setGlobalSessionTimeout(7200000);
sessionManager.setSessionValidationSchedulerEnabled(false);
sessionManager.setSessionValidationInterval(1800000);
sessionManager.setSessionDAO(sessionDao);
sessionManager.setSessionIdCookieEnabled(true);
sessionManager.setSessionIdCookie(sessionIdCookie);
List<SessionListener> sessionListeners = new ArrayList<>();
sessionListeners.add(sessionListener);
sessionManager.setSessionListeners(sessionListeners);
return sessionManager;
}
//配置session緩存管理器
@Bean
public CachingShiroSessionDao sessionDao() {
CachingShiroSessionDao sessionDao = new CachingShiroSessionDao();
sessionDao.setPrefix("shiro-session:");
sessionDao.setSeconds(1800);
return sessionDao;
}
//配置cookies
@Bean
public SimpleCookie sessionIdCookie() {
SimpleCookie simpleCookie = new SimpleCookie("sid");
//simpleCookie.setDomain(".sojson.com");
simpleCookie.setMaxAge(-1);
simpleCookie.setHttpOnly(true);
return simpleCookie;
}
//配置session監聽器
@Bean
public SessionListener sessionListener() {
return new ShiroSessionListener();
}
}
四、添加必要的配置類
添加一個判斷用戶可否登陸的過濾器,這個不是必須的,個人按情況添加。
package com.zxhy.shiro.filter;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 類的功能描述.
*
* @Date 2017/5/17
*/
public class RememberAuthenticationFilter extends FormAuthenticationFilter{
/**
* 這個方法決定了是否能讓用戶登錄
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = getSubject(request, response);
//如果 isAuthenticated 爲 false 證明不是登錄過的,同時 isRememberd 爲true 證明是沒登陸直接通過記住我功能進來的
if(!subject.isAuthenticated() && subject.isRemembered()){
//獲取session看看是不是空的
Session session = subject.getSession();
//隨便拿session的一個屬性來看session當前是否是空的,我用userId,你們的項目可以自行發揮
if(session.getAttribute("user") == null){
//如果是空的才初始化,否則每次都要初始化,項目得慢死
//這邊根據前面的前提假設,拿到的是username
// UserEntity userEntity = (UserEntity) subject.getPrincipal();
// session.setAttribute("user",userEntity);
//在這個方法裏面做初始化用戶上下文的事情,比如通過查詢數據庫來設置session值,你們自己發揮
//globalUserService.initUserContext(username, subject);
}
}
//這個方法本來只返回 subject.isAuthenticated() 現在我們加上 subject.isRemembered() 讓它同時也兼容remember這種情況
return subject.isAuthenticated() || subject.isRemembered();
}
}
添加自己的權限驗證類MyRealm
package com.zxhy.shiro.realm;
import com.zxhy.base.common.Constant;
import com.zxhy.sys.entity.MenuEntity;
import com.zxhy.sys.entity.RoleEntity;
import com.zxhy.sys.entity.UserEntity;
import com.zxhy.sys.service.MenuService;
import com.zxhy.sys.service.RoleService;
import com.zxhy.sys.service.UserRoleService;
import com.zxhy.sys.service.UserService;
import com.zxhy.utils.ShiroUtils;
import com.zxhy.utils.UsernamePasswordLoginTypeToken;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 類的功能描述.
* shiro 認證
* @Auther zxhy
* @Date 2017/4/27
*/
@Component
public class MyRealm extends AuthorizingRealm {
private static final Logger logger = LoggerFactory.getLogger(MyRealm.class);
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private MenuService menuService;
@Autowired
private UserRoleService userRoleService;
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
UserEntity user = (UserEntity) principals.getPrimaryPrincipal();
if(user !=null){
//根據用戶id查詢該用戶所有的角色,並加入到shiro的SimpleAuthorizationInfo
List<RoleEntity> roles = roleService.queryByUserId(user.getId(), Constant.YESNO.YES.getValue());
for (RoleEntity role:roles){
info.addRole(role.getId());
}
//查詢所有該用戶授權菜單,並加到shiro的SimpleAuthorizationInfo 做菜單按鈕權限控件
Set<String> permissions = new HashSet<>();
List<MenuEntity> menuEntities=null;
//超級管理員擁有最高權限
if(Constant.SUPERR_USER.equals(user.getId())){
menuEntities = menuService.queryList(new HashMap<String,Object>());
}else{
//普通帳戶權限查詢
menuEntities = menuService.queryByUserId(user.getId());
}
for (MenuEntity menuEntity:menuEntities){
if(StringUtils.isNotEmpty(menuEntity.getPermission())){
for(String per : menuEntity.getPermission().split(",")){
permissions.add(per);
}
}
}
info.addStringPermissions(permissions);
}
return info;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userLoginName= (String) token.getPrincipal();
UserEntity user = userService.queryByLoginName(userLoginName);
if(user == null){
throw new AuthenticationException("帳號密碼錯誤");
}
if(Constant.ABLE_STATUS.NO.getValue().equals(user.getStatus())){
throw new LockedAccountException("帳號被禁用,請聯繫管理員!");
}
//用戶對應的機構集合
List<String> baidList = userService.queryBapidByUserIdArray(user.getId());
//用戶對應的部門集合
List<String> bapidList= userService.queryBaidByUserIdArray(user.getId());
//用戶對應的電站集合
List<String> stationList= userService.queryStationByUserIdArray(user.getId());
user.setRoleIdList(userRoleService.queryRoleIdList(user.getId()));
user.setBapidList(bapidList);
user.setBaidList(baidList);
user.setStationIds(stationList);
SimpleAuthenticationInfo sainfo = new SimpleAuthenticationInfo(user,user.getPassWord(), ByteSource.Util.bytes(user.getSalt()),getName());
return sainfo;
}
@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher();
shaCredentialsMatcher.setHashAlgorithmName(ShiroUtils.algorithmName);
shaCredentialsMatcher.setHashIterations(ShiroUtils.hashIterations);
super.setCredentialsMatcher(shaCredentialsMatcher);
}
@Override
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException{
UsernamePasswordLoginTypeToken tokenType = (UsernamePasswordLoginTypeToken) token;
CredentialsMatcher cm = this.getCredentialsMatcher();
if (cm != null) {
if(!"1".equals(tokenType.getLoginType().getCode())){
if (!cm.doCredentialsMatch(token, info)) {
String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify credentials during authentication. If you do not wish for credentials to be examined, you can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}
}
添加session的緩存管理類,這個是用redis緩存的
package com.zxhy.shiro.redisSession;
import com.zxhy.base.utils.SerializeUtil;
import com.zxhy.utils.RedisUtil;
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.ValidatingSession;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* 針對自定義的ShiroSession的Redis CRUD操作,通過isChanged標識符,確定是否需要調用Update方法
* 通過配置securityManager在屬性cacheManager查找從緩存中查找Session是否存在,如果找不到才調用下面方法
* Shiro內部相應的組件(DefaultSecurityManager)會自動檢測相應的對象(如Realm)是否實現了CacheManagerAware並自動注入相應的CacheManager。
*/
@Component
public class CachingShiroSessionDao extends CachingSessionDAO {
private static final Logger logger = LoggerFactory.getLogger(CachingShiroSessionDao.class);
@Autowired
private RedisUtil redisUtil;
// 保存到Redis中key的前綴 prefix+sessionId
private String prefix = "";
// 設置會話的過期時間
private int seconds = 0;
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public void setSeconds(int seconds) {
this.seconds = seconds;
}
/**
* 重寫CachingSessionDAO中readSession方法,如果Session中沒有登陸信息就調用doReadSession方法從Redis中重讀
*/
@Override
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session session = getCachedSession(sessionId);
if (session == null
|| session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null) {
session = this.doReadSession(sessionId);
if (session == null) {
throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
} else {
// 緩存
cache(session, session.getId());
}
}
return session;
}
/**
* 根據會話ID獲取會話
*
* @param sessionId 會話ID
* @return ShiroSession
*/
@Override
protected Session doReadSession(Serializable sessionId) {
Session session = null;
String key = prefix + sessionId;
try {
if( redisUtil.get(key) != null ){
session = (Session) SerializeUtil.deserialize(redisUtil.get(key).toString());
}else{
logger.warn("讀取Session失效,sessionId:"+sessionId);
}
//logger.info("sessionId {} name {} 被讀取", sessionId, session.getClass().getName());
} catch (Exception e) {
logger.warn("讀取Session失敗,sessionId:"+sessionId, e);
}
return session;
}
/**
* 如DefaultSessionManager在創建完session後會調用該方法;
* 如保存到關係數據庫/文件系統/NoSQL數據庫;即可以實現會話的持久化;
* 返回會話ID;主要此處返回的ID.equals(session.getId());
*/
@Override
protected Serializable doCreate(Session session) {
// 創建一個Id並設置給Session
Serializable sessionId = this.generateSessionId(session);
assignSessionId(session, sessionId);
try {
// session由Redis緩存失效決定,這裏只是簡單標識
session.setTimeout(seconds);
redisUtil.set(prefix + sessionId,SerializeUtil.serialize(session),seconds);
logger.info("sessionId {} name {} 被創建", sessionId, session.getClass().getName());
} catch (Exception e) {
logger.warn("創建Session失敗", e);
}
return sessionId;
}
/**
* 更新會話;如更新會話最後訪問時間/停止會話/設置超時時間/設置移除屬性等會調用
*/
@Override
protected void doUpdate(Session session) {
//如果會話過期/停止 沒必要再更新了
try {
if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()) {
return;
}
} catch (Exception e) {
logger.error("ValidatingSession error");
}
try {
try {
redisUtil.set(prefix+session.getId(), SerializeUtil.serialize(session),seconds);
//logger.info("sessionId {} name {} 被更新", session.getId(), session.getClass().getName());
} catch (Exception e) {
logger.info("sessionId {} name {} 更新異常", session.getId(), session.getClass().getName());
throw e;
}
} catch (Exception e) {
logger.warn("更新Session失敗", e);
}
}
/**
* 刪除會話;當會話過期/會話停止(如用戶退出時)會調用
*/
@Override
protected void doDelete(Session session) {
try {
redisUtil.del(prefix + session.getId());
logger.debug("Session {} 被刪除", session.getId());
} catch (Exception e) {
logger.warn("刪除Session失敗", e);
}
}
/**
* 刪除cache中緩存的Session
*/
public void uncache(Serializable sessionId) {
Session session = this.readSession(sessionId);
super.uncache(session);
logger.info("取消session {} 的緩存", sessionId);
}
/**
* 獲取當前所有活躍用戶,如果用戶量多此方法影響性能
*/
/* @Override
public Collection<Session> getActiveSessions() {
Jedis jedis = null;
try {
jedis = jedisUtils.getResource();
Set<String> keys = jedis.keys(prefix + "*");
if (CollectionUtils.isEmpty(keys)) {
return null;
}
List<String> valueList = jedis.mget(keys.toArray(new String[0]));
return SerializeUtils.deserializeFromStringController(valueList);
} catch (Exception e) {
logger.warn("統計Session信息失敗", e);
} finally {
jedisUtils.returnResource(jedis);
}
return null;
}*/
}
添加session的管理類
/**
* @Title:
* @Author:admin
* @Version:1.0
* @Date:2018/8/14
* @Company:陝西致信恆源能源科技有限責任公司
*/
package com.zxhy.shiro.redisSession;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
/**
* Created by Palerock
*/
public class SessionManager extends DefaultWebSessionManager {
private static final Logger log = LoggerFactory.getLogger(DefaultWebSessionManager.class);
private String authorization = "X-Token";
/**
* 重寫獲取sessionId的方法調用當前Manager的獲取方法
*
* @param request
* @param response
* @return
*/
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
return this.getReferencedSessionId(request, response);
}
/**
* 獲取sessionId從請求中
*
* @param request
* @param response
* @return
*/
private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
String id = this.getSessionIdCookieValue(request, response);
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "cookie");
} else {
id = this.getUriPathSegmentParamValue(request, "JSESSIONID");
if (id == null) {
// 獲取請求頭中的session
id = WebUtils.toHttp(request).getHeader(this.authorization);
if (id == null) {
String name = this.getSessionIdName();
id = request.getParameter(name);
if (id == null) {
id = request.getParameter(name.toLowerCase());
}
}
}
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "url");
}
}
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
}
return id;
}
// copy super
private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
if (!this.isSessionIdCookieEnabled()) {
log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie.");
return null;
} else if (!(request instanceof HttpServletRequest)) {
log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie. Returning null.");
return null;
} else {
HttpServletRequest httpRequest = (HttpServletRequest) request;
return this.getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
}
}
// copy super
private String getUriPathSegmentParamValue(ServletRequest servletRequest, String paramName) {
if (!(servletRequest instanceof HttpServletRequest)) {
return null;
} else {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String uri = request.getRequestURI();
if (uri == null) {
return null;
} else {
int queryStartIndex = uri.indexOf(63);
if (queryStartIndex >= 0) {
uri = uri.substring(0, queryStartIndex);
}
int index = uri.indexOf(59);
if (index < 0) {
return null;
} else {
String TOKEN = paramName + "=";
uri = uri.substring(index + 1);
index = uri.lastIndexOf(TOKEN);
if (index < 0) {
return null;
} else {
uri = uri.substring(index + TOKEN.length());
index = uri.indexOf(59);
if (index >= 0) {
uri = uri.substring(0, index);
}
return uri;
}
}
}
}
}
// copy super
private String getSessionIdName() {
String name = this.getSessionIdCookie() != null ? this.getSessionIdCookie().getName() : null;
if (name == null) {
name = "JSESSIONID";
}
return name;
}
}
最後添加session的監聽器
package com.zxhy.shiro.redisSession;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 類ShiroSessionListener的功能描述:
* 發現用戶登出後,Session沒有從Redis中銷燬,雖然當前重新new了一個,但會對統計帶來干擾,通過SessionListener解決這個問題
* @auther zxhy
* @date 2017-09-25 11:45:11
*/
public class ShiroSessionListener extends SessionListenerAdapter {
private static final Logger logger = LoggerFactory.getLogger(ShiroSessionListener.class);
@Autowired
private CachingShiroSessionDao sessionDao;
@Override
public void onStart(Session session) {
// 會話創建時觸發
logger.info("ShiroSessionListener session {} 被創建", session.getId());
}
@Override
public void onStop(Session session) {
sessionDao.delete(session);
// 會話被停止時觸發
logger.info("ShiroSessionListener session {} 被銷燬", session.getId());
}
@Override
public void onExpiration(Session session) {
sessionDao.delete(session);
//會話過期時觸發
logger.info("ShiroSessionListener session {} 過期", session.getId());
}
}
這樣就完成了整個Shiro的集成
五、添加方便操作的Shiro工具類
package com.zxhy.utils;
import com.zxhy.sys.entity.UserEntity;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
/**
* 類ShiroUtils的功能描述:
* Shiro工具類
* @auther zxhy
* @date 2017-08-25 16:19:35
*/
public class ShiroUtils {
/** 加密算法 */
public final static String algorithmName = "SHA-256";
/**
* 加密散列次數
*/
public static final int hashIterations= 1;
public static String EncodeSalt(String password, String salt) {
return new SimpleHash(algorithmName, password, salt, hashIterations).toString();
}
public static Session getSession() {
return SecurityUtils.getSubject().getSession();
}
public static Subject getSubject() {
return SecurityUtils.getSubject();
}
public static UserEntity getUserEntity() {
return (UserEntity) SecurityUtils.getSubject().getPrincipal();
}
public static String getUserId() {
return getUserEntity().getId();
}
public static void setSessionAttribute(Object key, Object value) {
getSession().setAttribute(key, value);
}
public static Object getSessionAttribute(Object key) {
return getSession().getAttribute(key);
}
public static boolean isLogin() {
return SecurityUtils.getSubject().getPrincipal() != null;
}
public static void logout() {
SecurityUtils.getSubject().logout();
}
public static String getKaptcha(String key) {
String kaptcha = getSessionAttribute(key).toString();
getSession().removeAttribute(key);
return kaptcha;
}
}
六、登錄測試和用註解@RequiresPermissions添加權限測試
/**
* 登錄
*/
@ResponseBody
@RequestMapping(value = "/login/login", method = RequestMethod.POST)
public Result login(@RequestBody UserEntity userEntity, String captcha, boolean isRememberMe) throws IOException {
try {
UsernamePasswordLoginTypeToken token = token = new UsernamePasswordLoginTypeToken(userEntity.getLoginName(), userEntity.getPassWord());
Subject subject = ShiroUtils.getSubject();
token.setRememberMe(isRememberMe);
subject.login(token);
} catch (UnknownAccountException e) {
return Result.error(e.getMessage());
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
return Result.error("賬號或密碼不正確!");
} catch (LockedAccountException e) {
return Result.error("賬號已被鎖定,請聯繫管理員!");
} catch (AuthenticationException e) {
return Result.error(e.getMessage());
}
}
return Result.ok(map);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
@RequiresPermissions("sys:code:info")
@SysLog("查看字典")
public Result info(@PathVariable("id") String id){
CodeEntity code = codeService.queryObject(id);
return Result.ok().put("code", code);
}