一.背景
公司新項目淘寶客返利項目烛卧,由我負責后端開發(fā)接口,經(jīng)過了解残吩,淘寶客項目分為移動端(IOS,安卓)和PC端(后臺管理頁面)倘核,移動端要求登陸后30天內(nèi)免登陸泣侮,所以我選擇了用shiro來實現(xiàn)權限控制。
二.選用技術
基礎框架:springmvc+mybatis
安全框架:shiro
存儲:mysql+redis
三.實現(xiàn)流程
四.具體實現(xiàn)
問題一:
shiro是通過以來cookie 來實現(xiàn)的登陸,在app端是沒有cookie這一說的紧唱,所以活尊,決定用shiro來生成token,完成登陸漏益。
解決方案:
在登陸的時候蛹锰,生成一個uuid當作token,把token當作key绰疤,userInfo當作value存入redis中铜犬,并返回給前端token,每次請求必須把token帶入請求頭中轻庆。
public ResponseEntity login(@RequestBody LoginUserEntity loginUserEntity) {
if (StringUtils.isEmpty(loginUserEntity.getMobile()) && StringUtils.isEmpty(loginUserEntity.getUnionId())) {
return new ResponseEntity<>(new Result<>(ErrorCode.ILLEGAL_PARAMETER), HttpStatus.OK);
}
try {
TbkUserNameToken token = new TbkUserNameToken(loginUserEntity);
SecurityUtils.getSubject().login(token);
} catch (UnknownAccountException e) {
return new ResponseEntity(new Result<>(ErrorCode.USER_NO_EXIST), HttpStatus.OK);
} catch (IncorrectCredentialsException e) {
return new ResponseEntity(new Result<>(ErrorCode.PASSWORD_ERROR), HttpStatus.OK);
} catch (AccountException e) {
return new ResponseEntity(new Result<>(ErrorCode.MOBILE_CODE_ERROR), HttpStatus.OK);
} catch (UnsupportedTokenException e) {
return new ResponseEntity(new Result<>(ErrorCode.SYSTEM_ERROR), HttpStatus.OK);
}
UserInfo token = (UserInfo) SecurityUtils.getSubject().getPrincipal();
return new ResponseEntity<>(new Result<>(token), HttpStatus.OK);
}
問題二:
由于app是用戶入口癣猾,pc端是管理員入口,所以余爆,又一個簡單的權限驗證纷宇,我把這權限分為了3中,登陸后權限蛾方,不登陸權限像捶,登陸后admin權限,自定義了一個user的權限转捕,user['admin']
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,這個屬性是必須的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 身份認證失敗作岖,則跳轉到登錄頁面的配置 -->
<property name="loginUrl" value="/index"/>
<!-- 權限認證失敗,則跳轉到指定頁面 -->
<property name="unauthorizedUrl" value="/index"/>
<!--登陸成功-->
<property name="successUrl" value="/index"/>
<property name="filters">
<util:map>
<entry key="user" value-ref="tokenUserFilter"></entry>
</util:map>
</property>
<!-- Shiro連接約束配置,即過濾鏈的定義 -->
<property name="filterChainDefinitions">
<value>
/favorite/** = user
/autm/** = user['admin']
/admin/** = user['admin']
/jhs/** = user['admin']
/wx/** = user['admin']
/fans/** = user
/share/item/** = user
/account/** =user
/cash/** = user
/usercenter/** = user
/** = anon
</value>
</property>
當需要用戶登陸時候才能訪問的路徑五芝,我標志為user痘儡,當需要用戶登陸,并且具有admin權限的時候枢步,我對路徑驗證是需要user['admin']權限的沉删,其他的屬于開放權限 anon
問題三:
權限驗證時渐尿,如果出現(xiàn)問題,需要返回給前端明確的錯誤碼矾瑰。
protected boolean isAccessAllowed(ServletRequest req, ServletResponse response, Object mappedValue) {
HttpServletRequest request = (HttpServletRequest) req;
String token = request.getHeader("token");
if (StringUtils.isEmpty(token)) {
return false;
}
String s = redisClient.get(token);
if (StringUtils.isEmpty(s)) {
return false;
}
try {
UserInfo userInfo = JSON.parseObject(s, UserInfo.class);
if (userInfo == null) {
return false;
}
if (mappedValue == null) {
return true;
}
if (mappedValue != null && JSON.toJSONString(mappedValue).contains("admin")) {
return userInfo.isAdmin();
}
} catch (Exception e) {
logger.error("error access allowed", e);
}
return false;
}
返回fasle的時候砖茸,會調(diào)用下面方法:
@Override
protected boolean onAccessDenied(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
WebUtils.sendJson(JSON.toJSONString(new Result<>(ErrorCode.INVALID_USER)), response);
return false;
}
五.總結
本次項目時間非常緊,全部加起來只有1個月時間殴穴,要求全部功能上線凉夯,后端開發(fā)就我一個人,必須對接安卓采幌,ios以及pc的前端劲够,所以在設計shiro的登陸上,怎么簡單怎么來的休傍,從解決問題出發(fā)征绎,不影響用戶登陸為前提,用redis來控制過期時間磨取,雖然本次登陸設計非常簡單人柿,但是從目前上線情況來看,還是非趁ρ幔靠譜的凫岖。
shiro對spring 的集成網(wǎng)上有很多的文章,我只是提供我自己的統(tǒng)一登陸方案慰毅,所以只是貼出來部分代碼隘截。同時記錄下本次快速開發(fā)迭代的技術選型,希望后期不會有問題汹胃。不然我要跑路了