自定义登陆页面
在静态文件夹下新建一个login.html的简单登陆页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h2>自定义登录页面</h2>
<h3>表单登录</h3>
<form action="/user/login" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><button type="submit">登录</button></td>
</tr>
</table>
</form>
</body>
</html>
添加配置:
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserLoginService userLoginService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userLoginService)
.passwordEncoder(bCryptPasswordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.and()
// 授权的配置
.authorizeRequests()
// 登录页不需要身份验证
.antMatchers("/login.html").permitAll()
// 任何请求
.anyRequest()
// 都需要身份认证
.authenticated()
.and()// 关闭跨站请求伪造防护
.csrf().disable();
}
}
通过浏览器访问后台数据接口,跳到自己的登陆页:
输入数据库用户密码,成功获取数据
这里我们在没有登陆(未授权)的情况下,访问后台数据接口,直接跳转到了一个html的登陆页,(不管是自定义的登录页还是SpringSecurity自己的登陆页)在实际应用中,访问后台的请求可能是一个html的页面请求,也有可能是一个数据访问请求,当是一个html页面请求时,没有登陆的情况下我们希望让他跳转到登陆页,即返回一个html页面;当是一个数据访问请求时,这是返回一个htm页面是不合适的,应该返回带提示信息的数据,在发送这个请求的js代码中获取返回的数据,来做相应的处理,下面就来解决这个问题
在静态资源文件夹下新建一个登录页login_p.html
,home.html
表示登陆后才能访问的主页面,新建一个实体类格式化返回数据的json格式:
@Data
@ToString
public class RespBean {
private Integer status;
private String message;
private Object data;
public RespBean() {
}
public RespBean(Integer status, String message) {
this.status = status;
this.message = message;
}
public RespBean(Integer status, String message, Object data) {
this.status = status;
this.message = message;
this.data = data;
}
}
修改配置类
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/authentication/request")
.loginProcessingUrl("/user/login")
.and()
// 授权的配置
.authorizeRequests()
// 登录页不需要身份验证
.antMatchers("/authentication/request","/login_p.html").permitAll()
// 任何请求
.anyRequest()
// 都需要身份认证
.authenticated()
.and()// 关闭跨站请求伪造防护
.csrf().disable();
}
新建一个controller来处理未登录跳转请求/authentication/request
@RestController
public class AuthenticationController {
//在跳转之间,SpringSecurity将请求缓存到session中
private RequestCache requestCache = new HttpSessionRequestCache();
//页面跳转
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@RequestMapping("/authentication/request")
public RespBean requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest != null){
String redirectUrl = savedRequest.getRedirectUrl();
if (StringUtils.endsWithIgnoreCase(redirectUrl, ".html")){
System.out.println("引发跳转的请求是:"+redirectUrl);
redirectStrategy.sendRedirect(request, response, "/login_p.html");
}
}
return new RespBean(403,"请先登陆");
}
}
通过浏览器访问:http://localhost:9091/emp/basic/findAllUser
通过浏览器访问:http://localhost:9091/home.html
自定义登陆成功处理器
登陆成功之后返回Authentication
对象
/**
* @auther Mr.Liao
* @date 2019/8/19 20:03
*/
@Component
public class SuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
/**
* Authentication对象封装了请求信息,以及loadUserByUsername方法返回的UserDetails对象的信息
* @param request
* @param response
* @param authentication
* @throws IOException
* @throws ServletException
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(objectMapper.writeValueAsString(authentication));
writer.flush();
writer.close();
}
}
配置:
修改数据库查询语句,查询user的时候,同时查询出用户的role信息,同时取消实体里面的@JsonIgnore
注解,登陆成功之后返回的数据如下,sessionId保存在cookie中
自定义登陆失败的处理器
/**
* @auther Mr.Liao
* @date 2019/8/19 21:12
*/
@Component
public class FailureHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTH-8");
PrintWriter writer = response.getWriter();
writer.write(objectMapper.writeValueAsString(e.getMessage()));
writer.flush();
writer.close();
}
}
第三个参数AuthenticationException
是登陆认证过程中发生的所有异常信息,其有很多子接口,代表了各种异常
配置失败的处理器
从浏览器器登陆出入错误的用户或者密码结果为
这里我我们自定义了登陆成功和失败的处理方式,如果是前后端分离的情况下,成功或者失败我们一个返回一个实体类来表示成功或者失败的信息,前端拿到信息之后作出相应的处理,分别修改成功和失败处理器返回的数据:
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
RespBean resp = new RespBean(200, "登陆成功", authentication.getPrincipal());
writer.write(objectMapper.writeValueAsString(resp));
writer.flush();
writer.close();
}
这里我们返回了Principal
对象,里面包含了很多用户信息,可以根据实际情况需要来选择,或者在实体类上添加@JsonIgnore
注解来过滤掉某些敏感信息
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
RespBean resp = new RespBean(403, "登陆失败", e.getMessage());
writer.write(objectMapper.writeValueAsString(resp));
writer.flush();
writer.close();
}
失败的时候可以根据异常的类型来判断属于哪些异常给出提示信息
上面演示的是返回json数据的形式,当登陆成功或者失败时,并不是异步的登陆,需要跳转页面的时候,这个时候可以使用默认的成功或者失败处理器即可。