Spring Security(四)— 登录表单

在resource/static目录下创建一个login.html页面,如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <div>
        <form action="/doLogin" method="post">
            <h3>登录</h3>
            <div>
                <label>用户名:</label>
                <input type="text" name="uame" id="username" placeholder/>
            </div>
            <div>
                <label>密    码:</label>
                <input type="password" name="passwd" id="password" placeholder/>
            </div>
            <div>
                <input type="submit" value="登录"/>
            </div>

        </form>
    </div>
</body>
</html>

接下来定义两个接口:

@RestController
public class LoginController {
    @GetMapping("/index")
    public String index(){
        return "success";
    }
    @GetMapping("/hello")
    public String hello(){
        return "Hello Spring Security!";
    }
}

在提供一个配置类:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()//开启权限配置
                .anyRequest().authenticated()//所有请求都要走认证之后才能访问
                .and()//相当于返回HttpSecurity实例,重新开启新一轮的配置
                .formLogin()//开启表单配置
                .loginPage("/login.html")//配置登录页面地址
                .loginProcessingUrl("/doLogin")//配置登录接口
                .defaultSuccessUrl("/index")//登录成功后跳转地址
                .failureUrl("/login.html")//登录失败后跳转地址
                .usernameParameter("uname")//用户名的参数名称
                .passwordParameter("passwd")//密码的参数名称
                .permitAll()//跟登录有关的页面和接口不做拦截,直接通过。
                .and()
                .csrf().disable();//禁用csrf防御功能
    }
}

failureUrl表示登录失败后重定向到/login.html页面。重定向是一种客户端跳转,重定向不方便携带请求失败的异常信息(只能放在URL中)。如果希望能够在前端展示请求失败的异常信息,可以使用failureForwardUrl 这种跳转是一种服务器跳转,可以携带登录异常的信息。如果登录失败,自动跳转回登录页后,就可以将错误信息展示出来。

无论是failureUrl 还是failureForwardUrl ,最终所配置的都是AuthenticationFailureHandler 接口的实现。Spring Security 用 AuthenticationFailureHandler 来规范登录失败的实现,接口如下:

public interface AuthenticationFailureHandler {
	//exception 表示登录失败的异常信息
	void onAuthenticationFailure(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException;
}

AuthenticationFailureHandler 一共提供了五个实现类:
在这里插入图片描述

  1. SimpleUrlAuthenticationFailureHandler 默认的处理逻辑就是重定向到登录页面,也可以通过forwardToDestination 属性重定向改为服务器跳转,failureUrl 方法的底层实现逻辑就是它。
  2. ExceptionMappingAuthenticationFailureHandler 可以实现根据不同的异常类型,映射到不同的路径
  3. ForwardAuthenticationFailureHandler 表示通过服务器端跳转来重新回到登录页面,failureForwardUrl 方法的底层实现逻辑就是它。
  4. AuthenticationEntryPointFailureHandler 通过 AuthenticationEntryPoint 来处理登录异常
  5. DelegatingAuthenticationFailureHandler 可以实现为不同的异常类型配置不同的登录失败处理回调。

如今大部分使用的时前后端分离,登陆失败就不需要页面跳转了,只需要返回JSON字符串给前端就行,此时可以通过自定义AuthenticationFailureHandler 的实现类来完成,如下:

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        Map<String, Object> resp = new HashMap<String, Object>();
        resp.put("status",500);
        resp.put("message","登录失败!"+exception.getMessage());
        ObjectMapper om = new ObjectMapper();
        String s = om.writeValueAsString(resp);
        response.getWriter().write(s);
    }
}
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/doLogin")
                .defaultSuccessUrl("/index")
//                .failureUrl("/login.html")
                .failureHandler(new MyAuthenticationFailureHandler())
                .usernameParameter("uname")
                .passwordParameter("passwd")
                .permitAll()
                .and()
                .csrf().disable();
    }
}

配置完成后,当用户再次登录失败,就不会进行页面跳转了,而是直接返回JSON字符串,如下图:
在这里插入图片描述

注销登录

Spring Security 中提供了注销页面,我们也可以自定义注销页面:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
				//......
                .and()
                .logout()//开启注销登录配置
                .logoutUrl("/logout")//指定注销登录请求地址,默认GET请求
                .invalidateHttpSession(true)//是否使Session失效 默认true
                .clearAuthentication(true)//是否清除认证信息 默认true
                .logoutSuccessUrl("/login.html")//注销登录后跳转页面
                .and()
                .csrf().disable();
    }
}

多注销请求,不需要页面跳转,将JSON字符串返回给前端。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                //......
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(//配置多个注销登录的请求
                        new AntPathRequestMatcher("/logout1","GET"),
                        new AntPathRequestMatcher("/logout2","POST")
                ))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutSuccessHandler((req, res,auth) -> {//注销成功后不需要页面跳转,将JSON字符串返回给前端
                    res.setContentType("application/json;charset=UTF-8");
                    Map<String, Object> resp = new HashMap<String, Object>();
                    resp.put("status",200);
                    resp.put("message","注销成功!");
                    ObjectMapper om = new ObjectMapper();
                    String s = om.writeValueAsString(resp);
                    res.getWriter().write(s);
                })
                .and()
                .csrf().disable();
    }
}

不同的注销地址返回不同的结果

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                //......
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout1","GET"),
                        new AntPathRequestMatcher("/logout2","POST")
                ))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .defaultLogoutSuccessHandlerFor((req, res,auth) -> {
                    res.setContentType("application/json;charset=UTF-8");
                    Map<String, Object> resp = new HashMap<String, Object>();
                    resp.put("status",200);
                    resp.put("message","logout1注销成功!");
                    ObjectMapper om = new ObjectMapper();
                    String s = om.writeValueAsString(resp);
                    res.getWriter().write(s);
                },new AntPathRequestMatcher("/logout1","GET"))
                .defaultLogoutSuccessHandlerFor((req, res,auth) -> {
                    res.setContentType("application/json;charset=UTF-8");
                    Map<String, Object> resp = new HashMap<String, Object>();
                    resp.put("status",200);
                    resp.put("message","logout2注销成功!");
                    ObjectMapper om = new ObjectMapper();
                    String s = om.writeValueAsString(resp);
                    res.getWriter().write(s);
                },new AntPathRequestMatcher("/logout2","POST"))
                .and()
                .csrf().disable();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值