[Spring Security] 前后端分离项目中后端登录代码的简单示例

一.前言

  • 环境:springboot 2.3.0、springsecurity 5.3.2
  • 这篇随笔可能对其他人很不友好,因为只贴了相关的代码,所以不建议大家参考,我只是写给自己看的,但是跟大家分享一下;
  • 如果代码有问题的话请告诉我,但是其他的如代码不规范什么的就不必了,我知道我很渣,存在很多问题;
  • 管理员这个实体类只有 idusernamepassword 这三个属性
  • 该代码没有权限控制,没有会话管理等其他的功能,只有登录验证功能,所以较为简单
  • 下面每个各种处理器都必须在第6点中的 MySecurityConfiguration 中注册,否则无法生效
  • 代码已经验证过,没问题

二.代码

导包(略……)

1.自定义登录逻辑(访问数据库)

/**
 * @author Chase Meng
 * @description: 自定义登录逻辑(访问数据库)
 * @created on 2020/9/7
 */
@Configuration
public class CustomizeUserDetailsService implements UserDetailsService {
    @Autowired
    private LoginOperate loginOperate;
    @Autowired
    private PasswordEncoder passwordEncoder;
//    @Autowired
//    private Encryption encryption;

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

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException  {
        Admin admin =loginOperate.findUserByUsername(username);
        if(username==null||username.equals("")){
            throw new RuntimeException("用户名不能为空!");
        }
        if(admin==null){
            throw new RuntimeException("用户名不存在!");
        }
        List<GrantedAuthority> authorities=new ArrayList<GrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority("ROLE_"+"normal"));  //由于没有设置授权,所以随便给一个身份normal
        User userDetails = new User(admin.getUsername(), passwordEncoder.encode(admin.getPassword()), authorities);
        return userDetails;
    }
}

2.自定义登录失败处理器

**
 * @author Chase Meng
 * @description: 自定义登录失败处理器
 * @created on 2020/9/7
 */
@Component
public class CustomizeAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        //super.onAuthenticationFailure(request, response, exception);
        Result result=new Result();
        if(exception instanceof BadCredentialsException){
            //密码错误
            result.setCode(1200);
            result.setMsg("密码错误!");
        }else if(exception instanceof InternalAuthenticationServiceException){
            //用户不存在
            result.setCode(1100);
            result.setMsg("不存在该用户!");
        }
        else if(exception instanceof AccountExpiredException){
            //账号过期
        }
        else if(exception instanceof CredentialsExpiredException){
            //密码过期
        }else if(exception instanceof DisabledException){
            //帐号不可用
        }else if(exception instanceof LockedException){
            //帐号锁定
        }else{
            //其他错误
        }
        result.setState(false);
        // 把result对象转成 json 格式 字符串 通过 response 以application/json;charset=UTF-8 格式写到响应里面去
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write(result.toJsonWhenDataIsNull());
    }
}
/**
 * 或者使用实现AuthenticationFailureHandler类的方法来定义
 */
//@Component
//public class CustomizeAuthenctiationFailureHandler implements AuthenticationFailureHandler {
//    @Override
//    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//        ......//    }
//}

3.自定义登录成功处理器

/**
 * @author Chase Meng
 * @description: 自定义登录成功处理器
 * @created on 2020/9/7
 */
@Component
public class CustomizeAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        Result result = new Result();
        result.setCode(2000);
        result.setMsg("登录成功!");
        result.setState(true);
        Admin admin=new Admin();
        result.setData("{"username" : ""+SecurityContextHolder.getContext().getAuthentication().getName()+""}");//响应数据携带用户名
        // 把result对象转成 json 格式 字符串 通过 response 以application/json;charset=UTF-8 格式写到响应里面去
        httpServletResponse.setContentType("application/json; charset=utf-8");
        PrintWriter out = httpServletResponse.getWriter();
        out.write(result.toJsonWhenDataIsNull());
    }
}
/**
 * 或者使用实现AuthenticationSuccessHandler类的方法来定义
 */
/*@Component
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        ......
    }
}*/

4.屏蔽重定向的登录页面,并返回统一的json格式的数据

/**
 * @author Chase Meng
 * @description: 屏蔽重定向的登录页面,并返回统一的json格式的数据
 * @created on 2020/9/7
 */
@Component
public class CustomizeAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        Result result=new Result();
        httpServletResponse.setContentType("application/json; charset=utf-8");
        result.setCode(1000);
        result.setMsg("没有登录权限,请先登录!");
        result.setState(false);
        PrintWriter out = httpServletResponse.getWriter();
        out.write(result.toJsonWhenDataIsNull());
    }
}

5.自定义注销成功处理器

/**
 * @author Chase Meng
 * @description: 自定义注销成功处理器
 * @created on 2020/9/7
 */
@Component
public class CustomizeLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        Result result=new Result();
        httpServletResponse.setContentType("application/json; charset=utf-8");
        result.setCode(1000);
        result.setMsg("注销成功!");
        result.setState(true);
        PrintWriter out = httpServletResponse.getWriter();
        out.write(result.toJsonWhenDataIsNull());
        httpServletResponse.getWriter().flush();
    }
}

6.自定义spring security配置

/**
 * @author Chase Meng
 * @Description 自定义spring security配置
 * @Date Create in 2020/09/07
 */

@EnableWebSecurity
public class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomizeAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Autowired
    private CustomizeAuthenctiationFailureHandler myAuthenctiationFailureHandler;
    @Autowired
    private CustomizeAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    private CustomizeLogoutSuccessHandler logoutSuccessHandler;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置拦截规则
        //按先后注册的先后顺序匹配,因此顺序要格外注意
        http.authorizeRequests()
                .antMatchers("/upload/picture").permitAll() //放行图片上传接口
                .antMatchers("/*d/**").authenticated()  //拦截所有对回收站的查询
                .antMatchers(HttpMethod.GET).permitAll()    //放行其他所有GET请求
                .anyRequest().authenticated()   //拦截其余请求
                .and()
                .csrf().disable();  // 禁用跨站攻击,否则允许通行的其他路径的除了get请求之外的都会被拦截(包括登录注销接口)
        //开启跨域
        http.cors();
        //开启自动配置登录
        http.formLogin().permitAll()
                .successHandler(myAuthenticationSuccessHandler) //注册自定义处理器
                .failureHandler(myAuthenctiationFailureHandler)
//                .loginPage("http://localhost:8080/")    //登录页(GET)
                .loginProcessingUrl("/user/login");    //登录接口(POST)

        //记住密码
//        http.rememberMe();

        //屏蔽Spring Security默认重定向登录页面以实现前后端分离功能
        http.exceptionHandling()
                .authenticationEntryPoint(myAuthenticationEntryPoint);//匿名用户访问无权限资源时的异常
//                .accessDeniedHandler();   //用来解决认证过的用户访问无权限资源时的异常
        http.logout().permitAll()
                .logoutSuccessHandler(logoutSuccessHandler)  //注册登录失败处理器
                .deleteCookies("JSESSIONID")    //登出后删除cookie
                .logoutUrl("/user/logout"); //登出接口(POST)
    }
}

 三.结果

数据库中只有一个管理员:

username:admin

password:abc123

  • 用户不存在

  • 密码错误

  • 登录成功

原文地址:https://www.cnblogs.com/chasemeng/p/13631055.html