异常
SS中的异常主要分为两种
AuthenticationException authenticationException;//认证异常
AccessDeniedException accessDeniedException;//授权异常
我们在数据库中插入数据
此时我们登录id为1的用户,
自定义异常处理
.and()
.exceptionHandling()
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("无权限访问。");
response.flushBuffer();
}
})
.authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("尚未认证,请进行认证操作!");
response.flushBuffer();
}
})
如果我们登录了其他的无权限的用户进行访问则出现。
源码分析:这里面涉及到一个类FilterSecurityInterceptor
里面的方法
public void invoke(FilterInvocation fi) throws IOException, ServletException {
if ((fi.getRequest() != null)
&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
&& observeOncePerRequest) {
// filter already applied to this request and user wants us to observe
// once-per-request handling, so don't re-do security checking
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
else {
// first time this request being called, so perform security checking
if (fi.getRequest() != null && observeOncePerRequest) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
}
//这里面会进行判断当前这个请求需要什么权限,用户有没有权限
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, null);
}
}
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
final boolean debug = logger.isDebugEnabled();
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException(
"Security invocation attempted for object "
+ object.getClass().getName()
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
+ getSecureObjectClass());
}
//下面这个方法判断当前这个请求路径是否需要什么权限
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
if (attributes == null || attributes.isEmpty()) {
if (rejectPublicInvocations) {
throw new IllegalArgumentException(
"Secure object invocation "
+ object
+ " was denied as public invocations are not allowed via this interceptor. "
+ "This indicates a configuration error because the "
+ "rejectPublicInvocations property is set to 'true'");
}
if (debug) {
logger.debug("Public object - authentication not attempted");
}
publishEvent(new PublicInvocationEvent(object));
return null; // no further work post-invocation
}
if (debug) {
logger.debug("Secure object: " + object + "; Attributes: " + attributes);
}
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage(
"AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"),
object, attributes);
}
Authentication authenticated = authenticateIfRequired();
// Attempt authorization
try {
//这里对当前的用户进行权限的判断
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
accessDeniedException));
throw accessDeniedException;
}
if (debug) {
logger.debug("Authorization successful");
}
if (publishAuthorizationSuccess) {
publishEvent(new AuthorizedEvent(object, attributes, authenticated));
}
// Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
attributes);
if (runAs == null) {
if (debug) {
logger.debug("RunAsManager did not change Authentication object");
}
// no further work post-invocation
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
attributes, object);
}
else {
if (debug) {
logger.debug("Switching to RunAs Authentication: " + runAs);
}
SecurityContext origCtx = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
SecurityContextHolder.getContext().setAuthentication(runAs);
// need to revert to token.Authenticated post-invocation
return new InterceptorStatusToken(origCtx, true, attributes, object);
}
}
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
权限管理
SpringSecurity
提供了两种权限管理的策略。
FilterSecurityInterceptor
:基于过滤器的权限管理,将请求拦截下来根据http请求的地址进行权限的校验你。这种方式是请求到达方法之前进行处理的。
MethodSecurityInterceptor
:基于方法级别的拦截处理,利用的是aop的思想,这种是请求到达方法在方法调用前执行。
基于方法的权限管理
下面这几个注解想要生效,必须在配置类上面加上@EnableGlobalMethodSecurity(jsr250Enabled = true,prePostEnabled = true,proxyTargetClass = true,securedEnabled = true)
表示启用这些注解。
@PreAuthorize("hasRole('admin')")
@PostAuthorize("")
@PreFilter("")
@GetMapping("/admin/test")
@PreAuthorize("hasRole('admin') and authentication.name=='dongmu'")
//#name取得是接口上面的参数
@PostAuthorize("authentication.name == #name")
@PreFilter("hasAuthority('admin')")
@ResponseBody
public String admin(String name){
return "这是一个admin用户才能访问的界面。";
}
@GetMapping("/public/filter")
//下面这个表达式表示只是保留值是奇数的结果
@PreFilter(value = "filterObject%2!=0",filterTarget = "list")
public void filter(@RequestParam("ids") List<Integer> list){
for (Integer integer : list) {
System.out.println(integer);
}
}
//下面这个是或的关系,只要拥有一个权限就可以访问这个接口
@Secured({
"ROLE_admin","ROLE_user"})
@GetMapping("/public/secure")
public void secure(){
System.out.println("secure测试");
}