從來沒接觸過shiro Java安全框架,突然有一天需要要用用戶登陸驗(yàn)證和用戶角色權(quán)限的任務(wù),而且是針對shiro 進(jìn)行整合,開始收到任務(wù),心都有點(diǎn)涼涼的。經(jīng)過一輪的搜索,感覺沒多大的收獲。很多用戶的角色都是寫在xml配置文件中。覺得太不人性化了,想換個(gè)用戶角色還得改xml?我覺得這么強(qiáng)大的框架應(yīng)該不可能這么狗血的存在。然后認(rèn)真的看文檔,發(fā)現(xiàn)真的是可以直接讀取數(shù)據(jù)庫的。我把我搭建的流程發(fā)布在此。有問題的可以交流交流。我寫的也并不是正確的,只能參考參考。

創(chuàng)新互聯(lián)建站技術(shù)團(tuán)隊(duì)10年來致力于為客戶提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)、品牌網(wǎng)站建設(shè)、成都全網(wǎng)營銷、搜索引擎SEO優(yōu)化等服務(wù)。經(jīng)過多年發(fā)展,公司擁有經(jīng)驗(yàn)豐富的技術(shù)團(tuán)隊(duì),先后服務(wù)、推廣了近千家網(wǎng)站,包括各類中小企業(yè)、企事單位、高校等機(jī)構(gòu)單位。
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ù)庫中讀取用戶的角色權(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", "沒有授權(quán),請先登錄", 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", "抱歉,您沒有該權(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ù)訪問路徑獲取權(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("登錄失敗!無效的賬號或密碼!");
} catch (IncorrectCredentialsException ie) {
tokens.clear();
renderFailedResult("用戶已注銷!");
} catch(LockedAccountException le){
tokens.clear();
renderFailedResult("賬號被鎖定!");
} catch (RuntimeException re) {
re.printStackTrace();
tokens.clear();
renderFailedResult("登錄失敗!");
}
}
數(shù)據(jù)庫可以自己去設(shè)計(jì),這里就不提供了。
參照上面的去整合框架,便可以使用了,這樣搭建適合多種框架的整合。
登陸成功后獲取 Subject 對象.
然后通過 Subject 對象來判斷當(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;
}
好在配置簡單,以前做一個(gè)權(quán)限模塊要寫好多代碼。現(xiàn)在spring security好像是集成了shiro的功能,實(shí)現(xiàn)了代碼量更少,高速開發(fā)的目的。
不需要第三方包,java自帶就有。我給你個(gè)例子。
import?java.security.InvalidKeyException;
import?java.security.NoSuchAlgorithmException;
import?java.security.Security;
import?javax.crypto.BadPaddingException;
import?javax.crypto.Cipher;
import?javax.crypto.IllegalBlockSizeException;
import?javax.crypto.KeyGenerator;
import?javax.crypto.NoSuchPaddingException;
import?javax.crypto.SecretKey;
public?class?EncrypDES?{
//KeyGenerator?提供對稱密鑰生成器的功能,支持各種算法
private?KeyGenerator?keygen;
//SecretKey?負(fù)責(zé)保存對稱密鑰
private?SecretKey?deskey;
//Cipher負(fù)責(zé)完成加密或解密工作
private?Cipher?c;
//該字節(jié)數(shù)組負(fù)責(zé)保存加密的結(jié)果
private?byte[]?cipherByte;
public?EncrypDES()?throws?NoSuchAlgorithmException,?NoSuchPaddingException{
Security.addProvider(new?com.sun.crypto.provider.SunJCE());
//實(shí)例化支持DES算法的密鑰生成器(算法名稱命名需按規(guī)定,否則拋出異常)
keygen?=?KeyGenerator.getInstance("DES");
//生成密鑰
deskey?=?keygen.generateKey();
//生成Cipher對象,指定其支持的DES算法
c?=?Cipher.getInstance("DES");
}
/**
?*?對字符串加密
?*?
?*?@param?str
?*?@return
?*?@throws?InvalidKeyException
?*?@throws?IllegalBlockSizeException
?*?@throws?BadPaddingException
?*/
public?byte[]?Encrytor(String?str)?throws?InvalidKeyException,
IllegalBlockSizeException,?BadPaddingException?{
//?根據(jù)密鑰,對Cipher對象進(jìn)行初始化,ENCRYPT_MODE表示加密模式
c.init(Cipher.ENCRYPT_MODE,?deskey);
byte[]?src?=?str.getBytes();
//?加密,結(jié)果保存進(jìn)cipherByte
cipherByte?=?c.doFinal(src);
return?cipherByte;
}
/**
?*?對字符串解密
?*?
?*?@param?buff
?*?@return
?*?@throws?InvalidKeyException
?*?@throws?IllegalBlockSizeException
?*?@throws?BadPaddingException
?*/
public?byte[]?Decryptor(byte[]?buff)?throws?InvalidKeyException,
IllegalBlockSizeException,?BadPaddingException?{
//?根據(jù)密鑰,對Cipher對象進(jìn)行初始化,DECRYPT_MODE表示加密模式
c.init(Cipher.DECRYPT_MODE,?deskey);
cipherByte?=?c.doFinal(buff);
return?cipherByte;
}
/**
?*?@param?args
?*?@throws?NoSuchPaddingException?
?*?@throws?NoSuchAlgorithmException?
?*?@throws?BadPaddingException?
?*?@throws?IllegalBlockSizeException?
?*?@throws?InvalidKeyException?
?*/
public?static?void?main(String[]?args)?throws?Exception?{
EncrypDES?de1?=?new?EncrypDES();
String?msg?="郭XX-搞笑相聲全集";
byte[]?encontent?=?de1.Encrytor(msg);
byte[]?decontent?=?de1.Decryptor(encontent);
System.out.println("明文是:"?+?msg);
System.out.println("加密后:"?+?new?String(encontent));
System.out.println("解密后:"?+?new?String(decontent));
}
}
看看 開濤shiro第二十章
Subject工廠
Java代碼
public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
public Subject createSubject(SubjectContext context) {
//不創(chuàng)建session
context.setSessionCreationEnabled(false);
return super.createSubject(context);
}
}
!-- 會(huì)話管理器 --
bean id="sessionManager" class="org.apache.shiro.session.mgt.DefaultSessionManager"
property name="sessionValidationSchedulerEnabled" value="false"/
/bean
系統(tǒng)框架使用的springmvc 。。。。
在controller層上做了攔截器,添加了自定義標(biāo)簽,使用了該標(biāo)簽則需要校驗(yàn)session是否過期,過期則跳轉(zhuǎn)至登錄頁面,但是系統(tǒng)用到了shiro,請問在java代碼中如何判斷seesion已經(jīng)過期
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
//過期,則跳轉(zhuǎn)登錄頁面重新登錄
if () { //就是這里不知道如何寫!!!!!!!!!!!!!!!!!!!!!
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
希望能幫到樓主, 謝謝
網(wǎng)站欄目:shiro的java代碼,shirodkar
文章轉(zhuǎn)載:http://chinadenli.net/article4/dsehoie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供動(dòng)態(tài)網(wǎng)站、App設(shè)計(jì)、軟件開發(fā)、Google、網(wǎng)站設(shè)計(jì)公司、靜態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)