在使用Spring Security的时候我们可能会自定义一个AuthenticationEntryPoint类的实现类,配置该类能够对匿名用户进行拦截并返回对应数据。当用户访问被保护资源的时候,在过滤器中被发现是匿名用户则会由这个类进行处理。
但是添加了这个处理类后默认的登录页面被拦截了,因为是使用匿名用户进行访问导致触发了异常(InsufficientAuthenticationException),然后被交由自定义的AuthenticationEntryPoint实现类进行处理,导致无法进行登录操作。经过测试,发现在配置中开启表单验证可以正常获取认证后的信息。
进行简略的配置 以对该情况进行梳理
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
@RestController
public class DemoController {
@RequestMapping("/index")
public String index() {
return "Spring Security";
}
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("123456")
.roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
// 添加请求失败处理程序
.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPointImpl())
.and()
// 拦截全部请求
.authorizeRequests().anyRequest().authenticated();
}
}
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, org.springframework.security.core.AuthenticationException e) throws IOException, ServletException {
e.printStackTrace();
}
}
由于我们没有自定义登录页面,所以默认地址是/login
对登录地址进行请求出现如下情况:
现在,在配置中开启表单验证 formLogin()(开启后默认地址为 /login),并使用POST请求对接口进行访问,发现并未出现异常。
基于以上情况我们可以自定义一张登录页面来解决这个问题
补充依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
补充控制器接口
@RequestMapping("/login")
public ModelAndView login() {
return new ModelAndView("login");
}
创建登录页面 login.html
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Please sign in</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous">
</head>
<body>
<div class="container">
<form class="form-signin" method="post" action="/login">
<h2 class="form-signin-heading">Please sign in</h2>
<p>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required="" autofocus="">
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required="">
</p>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
</body>
</html>
添加配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
// 开启表单验证 设置登录页面地址 登录成功后跳转到 /index
.formLogin().loginPage("/login").successForwardUrl("/index")
.and()
.authorizeRequests()
// 放行登录接口
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPointImpl());
}
完成以上配置后在浏览器中访问 /login
登录后访问自定义的接口 /index
修改AuthenticationEntryPoint实现类以对匿名用户返回对应的json数据
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse response, org.springframework.security.core.AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("{message:\"请求失败,请登录!\"}");
out.flush();
out.close();
}
}