二、Spring Security自定义用户认证流程

自定义登陆页面

在静态文件夹下新建一个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.htmlhome.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数据的形式,当登陆成功或者失败时,并不是异步的登陆,需要跳转页面的时候,这个时候可以使用默认的成功或者失败处理器即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值