Spring Security -- 自定义登录

Spring Security – 自定义登录

1 使用formLogin()

http
    .formLogin()
    .loginPage("/myLogin") // 自定义登录页面
    .loginProcessingUrl("/doLogin") // 自定义登录接口
    .permitAll();

2 自定义登录过滤器

SpringSecurity 默认使用的是 UsernamePasswordAuthenticationFilter 过滤器,我们可以实现 AbstractAuthenticationProcessingFilter 类来自定义登录逻辑。

public class CustomLoginFilter extends AbstractAuthenticationProcessingFilter {

    //private static final String defaultFilterProcessesUrl = "/login";

    public CustomLoginFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {

        String method = request.getMethod();

        // 仅使用POST方法提交
        if(!"POST".equalsIgnoreCase(method)){
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        // 从Session中取出验证码
        String verify = (String) request.getSession().getAttribute("verify");
        // 从请求中获取验证码
        String code = request.getParameter("code");
        // 检查验证码
        checkCode(code,verify);

        // 从请求中获取 用户名及命名
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        username = username==null?"":username.trim();
        password = password==null?"":password.trim();


        // 生成 username+password 形式的 token
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        // 设置token详细信息
        authenticationToken.setDetails(authenticationDetailsSource.buildDetails(request));

        // 交给内部 AuthenticationManager 去认证,并返回 Authentication
        return this.getAuthenticationManager().authenticate(authenticationToken);
    }

    private void checkCode(String code,String verify){
        if(code==null || verify==null || "".equals(code) || verify.equalsIgnoreCase(code)){
            throw new AuthenticationServiceException("验证码错误:"+code);
        }
    }
}

实现 AuthenticationFailureHandler 接口,用于返回认证成功信息

public class LoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        onAuthenticationSuccess(request, response, authentication);
        chain.doFilter(request,response);
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        HashMap<String, Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","登陆成功");
        //map.put("data",authentication.getDetails());
        PrintWriter out = response.getWriter();
        out.write(map.toString());
        out.flush();
        out.close();
    }
}

实现 AuthenticationFailureHandler 接口返回 认证失败信息

public class LoginAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        HashMap<String, Object> map = new HashMap<>();
        map.put("code",400);
        if (exception instanceof LockedException) {
            map.put("msg","账户被锁定,请联系管理员!");
        } else if (exception instanceof CredentialsExpiredException) {
            map.put("msg","密码过期,请联系管理员!");
        } else if (exception instanceof AccountExpiredException) {
            map.put("msg","账户过期,请联系管理员!");
        } else if (exception instanceof DisabledException) {
            map.put("msg","账户被禁用,请联系管理员!");
        } else if (exception instanceof BadCredentialsException) {
            map.put("msg","用户名或者密码输入错误,请重新输入!");
        }else {
            map.put("msg",exception.getMessage());
        }
        out.write(map.toString());
        out.flush();
        out.close();
    }
}

在SpringSecurity配置类中配置:
authenticationManagerBean()是WebSecurityConfigurerAdapter 中内置的,直接调用即可

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;
    @Autowired
    private LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler;

    @Autowired
    private LoginAuthenticationFailureHandler loginAuthenticationFailureHandler;


    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/login","/login/doLogin","/css/**","/js/**","/images/**")
                .permitAll()
                .anyRequest()
                .authenticated();
        // 将自定义的过滤器添加进去
        http.addFilterAt(customLoginFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public CustomLoginFilter customLoginFilter() throws Exception {
        CustomLoginFilter customLoginFilter = new CustomLoginFilter("/login/doLogin");
        customLoginFilter.setAuthenticationSuccessHandler(loginAuthenticationSuccessHandler);
        customLoginFilter.setAuthenticationFailureHandler(loginAuthenticationFailureHandler);
        customLoginFilter.setAuthenticationManager(authenticationManagerBean());
        return customLoginFilter;
    }


}

参考:
spring-security权限控制详解 .

最新版 Spring Security5.x 教程合集(顺序已经整理好).

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值