日常求贊,感謝老闆。
前段時間把畢設部署到了線上,果然當時懶惰沒認真做權限,部署完沒多久就被大佬看破瞭然後
好吧然後我就趁今天有時間修復下。
先說下現狀
當時爲了懶省勁(大家別學我)只在user表上加了一個字段判斷是否管理員身份,權限上只在部分接口上加了,現在想起來還真是漏洞百出。
其實就管理員和普通用戶兩種角色,但由於後端權限未做全導致現在存在的問題:
- 管理員權限泄露
- 普通用戶存在數據權限越權危險
想怎麼做
-
- 由於之前引入了shiro框架,針對用戶角色的限制可已通過增加shiro中自定義Realm來實現(之前沒寫),然後在只能管理員調用的接口前增加相應的註解來實現。
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String userName = (String) principalCollection.getPrimaryPrincipal(); User user = iservice.getUserByUserName(userName); if (user != null && user.getAdmin() != null && StringUtils.equals(user.getAdmin(), Constant.ROLE_ADMIN)) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.addRole(Constant.ROLE_ADMIN); return authorizationInfo; } return null; }
@RequiresRoles(Constant.ROLE_ADMIN)
這裏遇到了個問題:調用使用了@RequireRoles的接口時並未進入授權方法,解決辦法:
在spring-mvc註解中加入下面代碼:
<!--用來管理shiro一些bean的生命週期--> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean>
- 到這裏如果被加上@RequireRoles的接口被訪問時沒相應的角色,會拋出UnauthorizedException異常,改到這裏又發現還沒做統一異常處理,這裏稍微做個滿足需求簡單的:
public class MyExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView modelAndView = new ModelAndView(); Map<String, Object> result = new HashMap<>(4); if (e instanceof UnauthorizedException) { result.put("success", false); result.put("message", "抱歉您無此權限"); }else { //這裏應該使用統一返回數據格式,不應該每次都這樣拼裝 result.put("success", false); result.put("message", "系統錯誤請聯繫管理員"); } FastJsonJsonView view = new FastJsonJsonView(); view.setAttributesMap(result); modelAndView.setView(view); return modelAndView; } }
<!--統一異常處理--> <bean class="com.zll.ccp.config.MyExceptionHandler"/>
-
針對普通用戶只能操作自己的相關資源:
-
創建註解@RequireOwn並加在需要判斷是否操作的是自己資源的接口上
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequireOwn { //資源類型 ResourceType resourceType(); //資源的唯一身份認證在參數的位置 int keyIndexOf() default 0; }
-
然後編寫intercepter在調用具體接口前通過反射查看相應的接口方法上是否有@RequireOwn註解,並判斷身份是否符合。
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); RequireOwn requireOwn = method.getDeclaredAnnotation(RequireOwn.class); // RequireOwn requireOwn = method.getAnnotation(RequireOwn.class); HttpSession session = request.getSession(); if (requireOwn != null && !(boolean) session.getAttribute(Constant.ROLE_ADMIN)) { //有註解且不是管理員則需要驗明正身 //真正的身份 String userId = (String) session.getAttribute("userId"); String parameter = request.getParameter(requireOwn.paramName()); String userKey = getResourceOwnId(requireOwn, parameter); if (!StringUtils.equals(userId, userKey)) { throw new UnauthorizedException(); } } } return true; } /** * 根據註解提供的資源類型和獲取到的參數獲得該資源真正擁有者的身份id * @param requireOwn 註解類 * @param key 資源id * @return */ private String getResourceOwnId(RequireOwn requireOwn,String key) { String userKey=""; ResourceType resourceType = requireOwn.resourceType(); switch (resourceType) { case blog: Blog blog = blogMapper.getBlogById(key); if (blog!=null){ userKey = blog.getUserId(); } break; case comment: BlogComment blogCommentsId = blogCommentMapper.getBlogCommentsId(key); if (blogCommentsId!=null) { userKey = blogCommentsId.getUserId(); } break; case reply: BlogReply blogReplyByid = blogReplyMapper.getBlogReplyByid(key); if (blogReplyByid != null) { userKey = blogReplyByid.getFromUserId(); } break; case theme: Theme themeById = themeMapper.getThemeById(key); if (themeById != null) { userKey = themeById.getUserid(); } break; case main: MainPosts mainById = mainPostsMapper.getMainById(key); if (mainById!=null) { userKey=mainById.getUserid(); } break; default: MinorPosts minorById = minorPostsMapper.getMinorById(key); if (minorById!=null) { userKey=minorById.getUserid(); } } return userKey; }
3. 對於一些如參數是json類型的由於數量很少就直接修改原來代碼了。
-
總結
至此完成了權限的修正
項目地址:校園交流平臺
本文源碼:ccp
歡迎star
日常乞討
- 如果你認爲本文對你有幫助,還請「轉發/贊/star」,多謝
- 如果你還發現了更好或不同的想法,還請在留言區不吝賜教,一起探討交流修改,萬分感謝