SpringSecurity授权相关
授权准备工作
为了模拟授权操作,临时编写两个业务功能:
处理器代码:
// ProductController
@Controller
@RequestMapping("/product")
public class ProductController {
@RequestMapping("/findAll")
public String findAll() {
return "product-list";
}
}
// OrderController
@Controller
@RequestMapping("/order")
public class OrderController {
@RequestMapping("/findAll")
public String findAll() {
return "order-list";
}
}
aside.jsp页面:
<ul class="treeview-menu">
<li id="system-setting"><a href="${pageContext.request.contextPath}/product/findAll"> <i class="fa fa-circle-o"></i>
产品管理</a></li>
<li id="system-setting"><a href="${pageContext.request.contextPath}/order/findAll"> <i class="fa fa-circle-o"></i>
订单管理</a></li>
</ul>
动态展示菜单
在aside.jsp对每个菜单通过SpringSecurity标签库指定访问所需角色
权限控制使用security的动态标签<security:authorize>实现,通过指定角色的方式来决定某些菜单是否展示
<!-- 只有ROLE_PRODUCT跟ROLE_ADMIN才能访问 -->
<security:authorize access="hasAnyRole('ROLE_PRODUCT', 'ROLE_ADMIN')">
<li id="product-setting">
<a href="${pageContext.request.contextPath}/product/findAll">
<i class="fa fa-circle-o"></i> 产品管理
</a>
</li>
</security:authorize>
<!-- 只有ROLE_ORDER跟ROLE_ADMIN才能访问 -->
<security:authorize access="hasAnyRole('ROLE_ORDER', 'ROLE_ADMIN')">
<li id="order-setting">
<a href="${pageContext.request.contextPath}/order/findAll">
<i class="fa fa-circle-o"></i> 订单管理
</a>
</li>
</security:authorize>
通过这个标签,不同角色的用户在登录后会有不同的菜单展示,但是这并非真正的实现了授权,因为通过地址栏手动输入url仍然可以访问到系统不希望该角色访问的页面
授权操作
首先说明项目IOC容器的结构,如下图,首先任何web工程都有一个最大的web容器ServletContext,然后在此基础上产生spring IOC父容器跟子容器,http请求只能够访问子容器而不能访问父容器,想要访问父容器必须通过子容器调用,这也就是springMVC安全的地方。

说明:SpringSecurity可以通过注解的方式来控制类或者方法的访问权限。注解需要对应的注解支持,若注解放在
controller类中,对应注解支持应该放在mvc配置文件中,因为controller类是由mvc配置文件扫描并创建的,同
理,注解放在service类中,对应注解支持应该放在spring配置文件中。由于现在是模拟业务操作,并没有
service业务代码,所以就把注解放在controller类中了。
开启授权的注解支持
<!--
开启权限的注解支持
secured-annotations="enabled":SpringSecurity内部的权限控制注解开关
pre-post-annotations="enabled":spring指定的权限控制的注解开关
jsr250-annotations="enabled":开启java250注解支持
-->
<security:global-method-security secured-annotations="enabled"
pre-post-annotations="enabled"
jsr250-annotations="enabled"/>
由于是在Controller层添加权限控制注解,那么这段配置代码也应该放到Controller对应的spring-mvc配置文件中,而不是spring-security配置文件中,否则会失效
在对应类或者方法上添加注解
对应三种注解开关,注解的方式也有三种,如下
@Controller
@RequestMapping("/product")
public class ProductController {
// @Secured({"ROLE_PRODUCT", "ROLE_ADMIN"}) // springSecurity内部制定的注解
// @RolesAllowed({"ROLE_PRODUCT", "ROLE_ADMIN"}) // jsr250注解
@PreAuthorize("hasAnyRole('ROLE_PRODUCT', 'ROLE_ADMIN')") // spring的el表达式注解
@RequestMapping("/findAll")
public String findAll(){
return "product-list";
}
}
权限不足异常处理
经过上面的幸苦,权限问题已经基本解决了,但是还有一个问题,那就是每次权限不足都会出现403页面,这实在不美观,现在应该做的就是消灭它!

方式一:在spring-security.xml配置文件中处理
<security:http auto-config="true" use-expressions="true">
<!-- 处理403异常 -->
<security:access-denied-handler error-page="/403.jsp"/>
</security:http>
方式二:在web.xml中处理
<!-- 处理403异常 -->
<error-page>
<error-code>403</error-code>
<location>/403.jsp</location>
</error-page>
方式三:编写异常处理器
在controller包下面新建一个异常处理包,并创建异常处理类。按照如下的写法,就可以控制住异常跳转的页面
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class HandlerControllerException implements HandlerExceptionResolver {
/**
*
* @param httpServletRequest request
* @param httpServletResponse response
* @param o 出现异常的对象
* @param e 出现的异常信息
* @return ModelAndView
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView mv = new ModelAndView();
// 将异常信息放入request域中
mv.addObject("errMsg", e.getMessage());
// 指定不同异常跳转的页面
if (e instanceof AccessDeniedException) {
mv.setViewName("forward:/403.jsp"); // forward表示不经过视图解析器
} else {
mv.setViewName("forward:/500.jsp"); // forward表示不经过视图解析器
}
return mv;
}
}
上面的写法虽然可以,但是稍显麻烦,其实还可以使用下面的写法
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class HandlerControllerException2 {
@ExceptionHandler(AccessDeniedException.class)
public String except403Advice() {
return "forward:/403.jsp";
}
@ExceptionHandler(RuntimeException.class)
public String exceptOtherAdvice() {
return "forward:/500.jsp";
}
}
很明显,第二种方式代码更加清晰简便些。

浙公网安备 33010602011771号