??Apache Shiro是Java的一個(gè)安全框架。功能強(qiáng)大,使用簡(jiǎn)單的Java安全框架,它為開(kāi)發(fā)人員提供一個(gè)直觀而全面的認(rèn)證,授權(quán),加密及會(huì)話管理的解決方案。
創(chuàng)新互聯(lián)公司是創(chuàng)新、創(chuàng)意、研發(fā)型一體的綜合型網(wǎng)站建設(shè)公司,自成立以來(lái)公司不斷探索創(chuàng)新,始終堅(jiān)持為客戶提供滿意周到的服務(wù),在本地打下了良好的口碑,在過(guò)去的10余年時(shí)間我們累計(jì)服務(wù)了上千家以及全國(guó)政企客戶,如成都被動(dòng)防護(hù)網(wǎng)等企業(yè)單位,完善的項(xiàng)目管理流程,嚴(yán)格把控項(xiàng)目進(jìn)度與質(zhì)量監(jiān)控加上過(guò)硬的技術(shù)實(shí)力獲得客戶的一致贊揚(yáng)。
??Shiro基本功能點(diǎn)如下所示:
??Shiro工作流程如下所示:
??Shiro內(nèi)部架構(gòu)如下所示:
??本文實(shí)現(xiàn)源碼如下,歡迎Star和Fork。
??實(shí)現(xiàn)思路:用戶登錄時(shí)生成token信息,設(shè)置過(guò)期時(shí)間,使用Redis存儲(chǔ)。前端調(diào)用接口時(shí)將token作為參數(shù)傳給服務(wù)端,服務(wù)端根據(jù)token信息認(rèn)證用戶。
??自定義AuthFilter過(guò)濾器,繼承AuthenticatingFilter重寫(xiě)createToken、isAccessAllowed、onAccessDenied、onLoginFailure方法。
??AuthenticatingFilte類(lèi)executeLogin方法如下所示:
??用戶登錄時(shí)刪除舊Token信息,重新生成Token信息,退出登錄時(shí)刪除Token信息。 使用Redis存儲(chǔ)Token信息時(shí),同時(shí)存儲(chǔ)已用戶ID為鍵,Token為值和已Token為鍵、用戶ID為值的信息 。
??整體實(shí)現(xiàn)流程圖如下所示, 圖源來(lái)自參考鏈接一,侵刪 。
??實(shí)現(xiàn)思路:自定義ModularRealmAuthenticator管理多Realm,結(jié)合自定義認(rèn)證Token關(guān)聯(lián)不同的Realm。
??SecurityManager和ModularRealmAuthenticator配置如下:
??自定義認(rèn)證Token,重寫(xiě)getCredentials方法, 根據(jù)loginType返回不同的比較對(duì)象 。
??普通登陸Realm認(rèn)證邏輯如下所示:
??Token關(guān)聯(lián)的Realm認(rèn)證邏輯如下所示:
??自定義ModularRealmAuthenticator,管理多Realm,實(shí)現(xiàn)邏輯如下所示:
??用戶登錄,返回生成的token信息:
??用戶攜帶token信息查詢文章( 有對(duì)應(yīng)權(quán)限 ):
??用戶攜帶token信息刪除用戶( 無(wú)權(quán)限 ):
??用戶攜帶token信息退出登錄:
??用戶退出登錄后攜帶原token信息刪除用戶:
從來(lái)沒(méi)接觸過(guò)shiro Java安全框架,突然有一天需要要用用戶登陸驗(yàn)證和用戶角色權(quán)限的任務(wù),而且是針對(duì)shiro 進(jìn)行整合,開(kāi)始收到任務(wù),心都有點(diǎn)涼涼的。經(jīng)過(guò)一輪的搜索,感覺(jué)沒(méi)多大的收獲。很多用戶的角色都是寫(xiě)在xml配置文件中。覺(jué)得太不人性化了,想換個(gè)用戶角色還得改xml?我覺(jué)得這么強(qiáng)大的框架應(yīng)該不可能這么狗血的存在。然后認(rèn)真的看文檔,發(fā)現(xiàn)真的是可以直接讀取數(shù)據(jù)庫(kù)的。我把我搭建的流程發(fā)布在此。有問(wèn)題的可以交流交流。我寫(xiě)的也并不是正確的,只能參考參考。
1.web.xml的配置
listener
listener-classorg.apache.shiro.web.env.EnvironmentLoaderListener/listener-class
/listener
filter
filter-nameshiroFilter/filter-name
filter-classorg.apache.shiro.web.servlet.ShiroFilter/filter-class
/filter
filter-mapping
filter-nameshiroFilter/filter-name
url-pattern/*/url-pattern
/filter-mapping
2.shiro.ini配置
[main]
[filters]
#自定義realm
shiroAuthorizingRealm = com.frame.security.ShiroAuthorizingRealm
securityManager.realm = $shiroAuthorizingRealm
# 聲明一個(gè)自定義的用戶校驗(yàn)攔截器
customFormAuthenticationFilter = com.frame.security.CustomFormAuthenticationFilter
# 聲明一個(gè)自定義的用戶角色權(quán)限攔截器
customPermissionsAuthorizationFilter = com.frame.security.CustomPermissionsAuthorizationFilter
#cache
shiroCacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
shiroCacheManager.cacheManagerConfigFile = classpath:ehcache.xml
securityManager.cacheManager = $shiroCacheManager
#session
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.globalSessionTimeout = 1800000
securityManager = org.apache.shiro.web.mgt.DefaultWebSecurityManager
[urls]
/admin/user/login = anon
/admin/user/logout = anon
/admin/user/registered = anon
/admin/** = customFormAuthenticationFilter,customPermissionsAuthorizationFilter
從shiro.ini配置中可以看出,需要三個(gè)文件,分別為ShiroAuthorizingRealm.java(realm文件),CustomFormAuthenticationFilter.java(自定義用戶登陸驗(yàn)證文件),CustomPermissionsAuthorizationFilter(自定義用戶角色權(quán)限文件);
在urls配置中可以看出不需要攔截的url后面加上anon便可,但有先后順序。
緩存是使用ehcache
3.ehcache.xml配置
cache name="defaultCache" maxElementsInMemory="500"
maxElementsOnDisk="10000000" eternal="true" overflowToDisk="true"
diskSpoolBufferSizeMB="50" /
cache name="shiro-activeSessionCache" maxElementsInMemory="500"
maxElementsOnDisk="10000000" eternal="true" overflowToDisk="true"
diskSpoolBufferSizeMB="50" /
cache name="jdbcRealm.authorizationCache" maxElementsInMemory="500"
maxElementsOnDisk="10000000" eternal="true" overflowToDisk="true"
diskSpoolBufferSizeMB="50" /
cache name="authorization" maxElementsInMemory="500"
timeToLiveSeconds="3600" eternal="false" overflowToDisk="false" /
4.ShiroAuthorizingRealm.java
public class ShiroAuthorizingRealm extends AuthorizingRealm {
private AuthorityService authorityService = FrameContext.getBean(AuthorityService.class);
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("=======doGetAuthenticationInfo=======");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
String username = userToken.getUsername();
String password = String.valueOf(userToken.getPassword());
User user = User.dao.findFirst("select * from m_user where account = ?", username);
if (user != null) {//下面可以做一些登陸的操作,密碼錯(cuò)誤,用戶狀態(tài)等等
if(MD5Encoder.validPassword(password, user.getPassword())==false){
throw new UnknownAccountException();
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
} else {
return null;
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("=======doGetAuthorizationInfo=======");
User user = (User) principals.getPrimaryPrincipal();
if(user!=null){//從數(shù)據(jù)庫(kù)中讀取用戶的角色權(quán)限,
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
ListString perms = authorityService.getUrlByUser(user);
if(perms!=nullperms.size()0){//調(diào)用addStringPermissions方法把用戶的權(quán)限信息添加到info中,可以addRoles方法把用戶的角色添加到了info中
info.addStringPermissions(perms);
}
return info;
}
return null;
}
}
5.CustomFormAuthenticationFilter.java
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
private final static Logger log = Logger.getLogger(CustomFormAuthenticationFilter.class);
private static final String contentType = "application/json; charset=UTF-8";
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpRequest = WebUtils.toHttp(request);
HttpServletResponse httpResponse = WebUtils.toHttp(response);
if (isLoginRequest(request, response)) {
if (isLoginSubmission(request, response)) {
if (log.isTraceEnabled()) {
log.trace("Login submission detected. Attempting to execute login.");
}
return executeLogin(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Login page view.");
}
return true;
}
} else {
ResultObject result = new ResultObject(false, "401", "沒(méi)有授權(quán),請(qǐng)先登錄", null);
renderJson(httpResponse, result);
return false;
}
}
private void renderJson(HttpServletResponse response, Object object) {
String jsonText = JsonKit.toJson(object);
PrintWriter writer = null;
try {
response.setHeader("Pragma", "no-cache"); // HTTP/1.0 caches might not implement Cache-Control and might only implement Pragma: no-cache
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType(contentType);
writer = response.getWriter();
writer.write(jsonText);
writer.flush();
} catch (IOException e) {
throw new RenderException(e);
}
finally {
if (writer != null) {
writer.close();
}
}
}
}
6.CustomPermissionsAuthorizationFilter.java
public class CustomPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
private static final String contentType = "application/json; charset=UTF-8";
private AuthorityService authorityService = McmsContext.getBean(AuthorityService.class);
@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
if(getMappedValue(request)!=null){
return super.isAccessAllowed(request, response, getMappedValue(request));
}
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
// TODO Auto-generated method stub
HttpServletRequest httpRequest = WebUtils.toHttp(request);
HttpServletResponse httpResponse = WebUtils.toHttp(response);
String path = httpRequest.getServletPath();
Subject subject = getSubject(request, response);
if (subject.isPermitted(path)) {
return true;
} else {
ResultObject result = new ResultObject(false, "401", "抱歉,您沒(méi)有該權(quán)限!", null);
renderJson(httpResponse, result);
return false;
}
}
/**
* 得到mappedValue,相當(dāng)于perms[user:add]中的“user:add”
* @param path
* @return
*/
public String[] getMappedValue(ServletRequest request) {
HttpServletRequest req = (HttpServletRequest) request;
String path = req.getServletPath();
String code = getCodesByPath(path);
if(null == code) {
return null;
}
return new String[]{code};
}
/**
* 根據(jù)訪問(wèn)路徑獲取權(quán)限代碼
* @param path
* @return
*/
public String getCodesByPath(String path) {
User user = (User) SecurityUtils.getSubject().getPrincipal();
String pers = authorityService.getUrlByUserPath(path,user);
return Optional.ofNullable(pers).orElse(null);
}
private void renderJson(HttpServletResponse response, Object object) {
String jsonText = JsonKit.toJson(object);
PrintWriter writer = null;
try {
response.setHeader("Pragma", "no-cache"); // HTTP/1.0 caches might not implement Cache-Control and might only implement Pragma: no-cache
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType(contentType);
writer = response.getWriter();
writer.write(jsonText);
writer.flush();
} catch (IOException e) {
throw new RenderException(e);
}
finally {
if (writer != null) {
writer.close();
}
}
}
}
7.用戶登陸入口
public void login() {
String account = getPara("account");
String password = getPara("password");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken tokens = new UsernamePasswordToken(account, password);
tokens.setRememberMe(false);
try {
subject.login(tokens);
User user = (User) subject.getPrincipal();
loginSuccess(user);
UserVo userVo = convertToUserVO(user);
renderSucessResult(userVo);
} catch (UnknownAccountException ue) {
tokens.clear();
renderFailedResult("登錄失敗!無(wú)效的賬號(hào)或密碼!");
} catch (IncorrectCredentialsException ie) {
tokens.clear();
renderFailedResult("用戶已注銷(xiāo)!");
} catch(LockedAccountException le){
tokens.clear();
renderFailedResult("賬號(hào)被鎖定!");
} catch (RuntimeException re) {
re.printStackTrace();
tokens.clear();
renderFailedResult("登錄失??!");
}
}
數(shù)據(jù)庫(kù)可以自己去設(shè)計(jì),這里就不提供了。
參照上面的去整合框架,便可以使用了,這樣搭建適合多種框架的整合。
登陸成功后獲取 Subject 對(duì)象.
然后通過(guò) Subject 對(duì)象來(lái)判斷當(dāng)前用戶的角色/權(quán)限, 之后執(zhí)行不同的跳轉(zhuǎn)(直接在LoginAction中做).
我的登陸部分代碼:
Java代碼
UsernamePasswordToken token = new UsernamePasswordToken(name, password);
try {
SecurityUtils.getSubject().login(token);
Subject subject = SecurityUtils.getSubject();
// 這里可以調(diào)用subject 做判斷
System.out.println("--------------------------------------------------------------");
Boolean isadmin = subject.hasRole("admin");
log.info("是否為管理員:"+isadmin);
System.out.println("--------------------------------------------------------------");
String userId = (String)subject.getPrincipal();
User user = userService.getById(userId);
ShiroUser shiroUser = shiroUserService.getByDyId(userId);
if(shiroUser == null){
this.addActionError(getText("login.failure"));
return ERROR;
}else{
int used = shiroUser.getUsed();
if(used == 1){
this.addActionError(getText("login.noused"));
return ERROR;
}
}
Session session = subject.getSession(true);
session.setAttribute(LoginAction.USER_KEY, user);
session.setAttribute(LoginAction.SHIRO_USER_KEY, shiroUser);
log.info("set workflow define to session");
session.setAttribute("ptDefine", WorkflowContext.getPtDefine());
} catch (AuthenticationException e) {
log.info(e.getMessage());
this.addActionError(getText("login.failure"));
}
if (this.hasErrors()) {
log.info("login erro ...");
return ERROR;
}
系統(tǒng)框架使用的springmvc 。。。。
在controller層上做了攔截器,添加了自定義標(biāo)簽,使用了該標(biāo)簽則需要校驗(yàn)session是否過(guò)期,過(guò)期則跳轉(zhuǎn)至登錄頁(yè)面,但是系統(tǒng)用到了shiro,請(qǐng)問(wèn)在java代碼中如何判斷seesion已經(jīng)過(guò)期
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
//過(guò)期,則跳轉(zhuǎn)登錄頁(yè)面重新登錄
if () { //就是這里不知道如何寫(xiě)?。。。。。。。。。。。。。。。。。。。。?/p>
dosomething;。。。。
}
shiro配置如下:
!-- 會(huì)話DAO --
bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"
property name="activeSessionsCacheName" value="shiro-activeSessionCache"/
property name="sessionIdGenerator" ref="sessionIdGenerator"/
/bean
!-- 會(huì)話驗(yàn)證調(diào)度器 --
bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"
property name="sessionValidationInterval" value="1800000"/
property name="sessionManager" ref="sessionManager"/
/bean
!-- 會(huì)話管理器 --
bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"
property name="globalSessionTimeout" value="1800000"/!-- 回話有效時(shí)間30分鐘 --
property name="deleteInvalidSessions" value="true"/
property name="sessionValidationSchedulerEnabled" value="true"/
property name="sessionValidationScheduler" ref="sessionValidationScheduler"/
property name="sessionDAO" ref="sessionDAO"/
property name="sessionIdCookieEnabled" value="true"/
property name="sessionIdCookie" ref="sessionIdCookie"/
/bean
希望能幫到樓主, 謝謝
當(dāng)前題目:shriojava代碼 java shrio
瀏覽地址:http://muchs.cn/article12/hjdddc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機(jī)、搜索引擎優(yōu)化、網(wǎng)站營(yíng)銷(xiāo)、靜態(tài)網(wǎng)站、App設(shè)計(jì)、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)