SpringSecurity 使用6:自定义登录页、接口、结果

原创
2021/02/07 00:20
阅读数 7.2K

在之前的所有样例中,登录表单一直都是使用 Spring Security 提供的默认登录页,登录成功后也是默认的页面跳转。有时我们想要使用自定义的登录页,或者在前后端分离的开发方式中,前后端的数据交互通过 JSON 进行,这时登录成功后就不是页面跳转了,而是一段 JSON 提示。下面通过样例演示如何进行登录表单的个性化配置。

 

1、样例代码

我们这里会用到网页html,需要在 pom 引入 Thymeleaf 模板引擎。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

 

然后修改 Spring Security 配置,增加相关的自定义代码:

  • 将登录页改成使用自定义页面,并配置登录请求处理接口,以及用户密码提交时使用的参数名。
  • 自定义了登录成功、登录失败的处理逻辑,根据情况返回响应的 JSON 数据。
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
    // 指定密码的加密方式
    @SuppressWarnings("deprecation")
    @Bean
    PasswordEncoder passwordEncoder(){
        // 不对密码进行加密
        return NoOpPasswordEncoder.getInstance();
    }
 
    // 配置用户及其对应的角色
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("root").password("123").roles("DBA")
                .and()
                .withUser("admin").password("123").roles("ADMIN")
                .and()
                .withUser("hangge").password("123").roles("USER");
    }
 
    // 配置 URL 访问权限
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() // 开启 HttpSecurity 配置
            .antMatchers("/db/**").hasRole("DBA") // db/** 模式URL需DBA角色
            .antMatchers("/admin/**").hasRole("ADMIN") // admin/** 模式URL需ADMIN角色
            .antMatchers("/user/**").hasRole("USER") // user/** 模式URL需USER角色
            .anyRequest().authenticated() // 用户访问其它URL都必须认证后访问(登录后访问)
            .and().formLogin()  // 开启登录表单功能
            .loginPage("/login_page") // 使用自定义的登录页面,不再使用SpringSecurity提供的默认登录页
            .loginProcessingUrl("/login") // 配置登录请求处理接口,自定义登录页面、移动端登录都使用该接口
            .usernameParameter("name") // 修改认证所需的用户名的参数名(默认为username)
            .passwordParameter("passwd") // 修改认证所需的密码的参数名(默认为password)
            // 定义登录成功的处理逻辑(可以跳转到某一个页面,也可以返会一段 JSON)
            .successHandler(new AuthenticationSuccessHandler() {
                @Override
                public void onAuthenticationSuccess(HttpServletRequest req,
                                                    HttpServletResponse resp,
                                                    Authentication auth)
                        throws IOException, ServletException {
                    // 我们可以跳转到指定页面
                    // resp.sendRedirect("/index");
  
                    // 也可以返回一段JSON提示
                    // 获取当前登录用户的信息,在登录成功后,将当前登录用户的信息一起返回给客户端
                    Object principal = auth.getPrincipal();
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    resp.setStatus(200);
                    Map<String, Object> map = new HashMap<>();
                    map.put("status", 200);
                    map.put("msg", principal);
                    ObjectMapper om = new ObjectMapper();
                    out.write(om.writeValueAsString(map));
                    out.flush();
                    out.close();
                }
            })
            // 定义登录失败的处理逻辑(可以跳转到某一个页面,也可以返会一段 JSON)
            .failureHandler(new AuthenticationFailureHandler() {
                @Override
                public void onAuthenticationFailure(HttpServletRequest req,
                                                    HttpServletResponse resp,
                                                    AuthenticationException e)
                        throws IOException, ServletException {
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    resp.setStatus(401);
                    Map<String, Object> map = new HashMap<>();
                    // 通过异常参数可以获取登录失败的原因,进而给用户一个明确的提示。
                    map.put("status", 401);
                    if (e instanceof LockedException) {
                        map.put("msg", "账户被锁定,登录失败!");
                    }else if(e instanceof BadCredentialsException){
                        map.put("msg","账户名或密码输入错误,登录失败!");
                    }else if(e instanceof DisabledException){
                        map.put("msg","账户被禁用,登录失败!");
                    }else if(e instanceof AccountExpiredException){
                        map.put("msg","账户已过期,登录失败!");
                    }else if(e instanceof CredentialsExpiredException){
                        map.put("msg","密码已过期,登录失败!");
                    }else{
                        map.put("msg","登录失败!");
                    }
                    ObjectMapper mapper = new ObjectMapper();
                    out.write(mapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                }
            })
            .permitAll() // 允许访问登录表单、登录接口
            .and().csrf().disable(); // 关闭csrf
    }
}

(2)在 resource/templates 目录下创建一个登录页面 login_page.html,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="post">
        <div>
            <label>用户名</label>
            <input type="text" name="name"/>
        </div>
        <div>
            <label>密码</label>
            <input type="password" name="passwd"/>
        </div>
        <div>
            <input type="submit" value="登陆">
        </div>
    </form>
</body>
</html>

 (3)最后我们自定义一个 MVC 配置,并重写 addViewControllers 方法进行映射关系配置即可。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login_page").setViewName("login_page");
    }
}

 

2、运行测试 

(1) 随便访问一个接口,浏览器会自动跳转到我们自定义的登录页面:

(2)如果填写正确的用户名密码提交,则返回如下信息:

(3)如果填写错误的用户名密码条,则返回如下信息:

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部