自定义Handler
原创大约 3 分钟
自定义处理器
之前除了/login
接口能成功,另外两个都失败并不是因为代码问题,而是Spring Security默认是通过Web页面来实现页面逻辑跳转的。
但在前后端分离的开发模式中,页面跳转的逻辑后端已经不能直接控制了,而是通过返回状态码由前端来执行跳转,因此,需要对应用进行改造,自定义一个登录成功处理器。
要定义这样的处理器,只需要实现Spring Security预先提供的一系列接口XXXHandler
接口就行了。
package com.xiangwang.vmall.security;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义认证成功处理器
*
*/
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
System.out.println("登录成功");
// 前后端分离的调用方式
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("OK");
}
}
/**
* 自定义认证失败处理器
*
*/
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
System.out.println("user or password error");
// 前后端分离的调用方式
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("user or password error");
}
}
/**
* 自定义登出处理器
*
*/
@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
System.out.println("登出成功");
// 前后端分离的调用方式
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("OK");
}
}
然后修改WebSecurityConfiguration
代码,在FilterChain
中加上这些自定义的XXXHandler
。
......
@Autowired
private CustomAuthenticationSuccessHandler successHandler;
@Autowired
private CustomAuthenticationFailureHandler failureHandler;
@Autowired
private CustomLogoutSuccessHandler logoutSuccessHandler;
......
// 控制逻辑
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
// 如果允许有匿名的url通过,可以这样写,例如,注册验证码接口就不需要权限
.antMatchers("/verifyCode").permitAll()
// 设置登录页
.and().formLogin().loginPage("/login")
// 自定义登录成功、失败及登出处理器
.successHandler(successHandler).failureHandler(failureHandler).permitAll()
.and()
.logout().logoutUrl("/logout").deleteCookies("JSESSIONID")
.logoutSuccessHandler(logoutSuccessHandler).permitAll()
// 跨域访问
.and().cors();
// 关闭CSRF跨域
http.csrf().disable();
}
将LoginController
中所有的方法都去掉,登录及登出操作都能成功,权限已经做到了与Controller
中的业务逻辑无关了。
测试用户角色
在LoginController
中加入以下方法。
@GetMapping("/admin")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String admin() {
return "admin有ROLE_ADMIN角色";
}
@GetMapping("/manager")
@PreAuthorize("hasRole('ROLE_MANAGER')")
public String manager() {
return "manager有ROLE_MANAGER角色";
}
启动服务,用Postman访问后结果如下。
用
admin
登录,访问http://localhost:8080/admin正常,访问http://localhost:8080/manager出现Forbidden
错误。用
manager
登录,访问http://localhost:8080/manager正常,访问http://localhost:8080/admin出现Forbidden
错误。
这说明权限已经起作用了,但显示Forbidden
的方式不够友好,要修改它只需要实现AccessDeniedHandler
接口然后把它加到FilterChain
中去就行了。
package com.xiangwang.vmall.security;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义访问被拒绝
*
*/
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException exception) throws IOException, ServletException {
System.out.println("permission denied");
// 前后端分离的调用方式
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("permission denied");
}
}
修改WebSecurityConfiguration
代码。
......
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
......
// 控制逻辑
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
......
// 配置无权访问的自定义处理器
.and().exceptionHandling().accessDeniedHandler(accessDeniedHandler)
......
}
现在的提示现在已经比刚才要友好一些了。
感谢支持
更多内容,请移步《超级个体》。