SpringBoot2.0.3 + SpringSecurity5.0.6 + vue 前后端分离认证授权

Posted 来兮子宁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot2.0.3 + SpringSecurity5.0.6 + vue 前后端分离认证授权相关的知识,希望对你有一定的参考价值。

  新项目引入安全控制

  项目中新近添加了Spring Security安全组件,前期没怎么用过,加之新版本少有参考,踩坑四天,终完成初步解决方案.其实很简单,Spring Security5相比之前版本少了许多配置,操作起来更轻量

  MariaDb登录配置加密策略

  SpringSecurity5在执行登录认证时,需预设加密策略.

  坑一:加密策略配置,验密始终不通过,报错401

  坑二:本地重写的UserDetailsService实现类在注入的时候找不到,目前图省事直接用了 @Qualifier制定

  其它,实体类user实现UserDetails,role实现GrantedAuthority与之前版本并有太大变动,可参考很多,不做赘述

  代码如下:  

  /**
     * 项目中重写的 UserDetailsService接口的实现类,需指定
     */
    @Qualifier("userService")
    @Autowired
    private UserDetailsService userDetailsService;
    /**
     * 初始验证登录  从内存中取密码
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());

    }

  跨域的问题

  Springboot2.0.3处理跨域的时候特别简单,只需要在

  @EnableWebSecurity
  @Configuration
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  @Order(-1)  
  等修饰下的配置类中的HttpSecurity中,加上 cors()即可,完全不需要写过滤器包装HttpServletResponse的操作  

  登录报错403,权限不足
  这里的解决方案很多,因为本文项目不大,直接关闭 csrf (跨站请求伪造)即可
  同上,csrf().disable()即可.
   
  最大坑--跨域打开,每次登录返回为匿名用户anonymousUser
  问题描述:
    跨域已打开,使用Swagger访问都没有问题,前后端分离时,SpringSecurity也正常工作,最终还是登录不成功,返回匿名用户
    关闭匿名用户即 anonymous().disable(),直接报错401,用户名或密码错误
  遇到这个问题,一直纠结在跨域上,却没有深入去查看前端http请求上给出的信息,原因很简单,登录时重定向的问题
  在HttpSecurity中,在选择 formLogin()时,其后会选择各种成功失败的url,然后代码上去实现相关的接口,其实就入坑了.
  注意:在前端使用ajax登录时,SpringSecurity只能通过重写相关成功/失败/退出等的处理器handler来完成相关处理逻辑
  完整配置类代码:
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(-1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
    @Autowired
    CustomizeAuthenticationFailHandler customizeAuthenticationFailHandler;
    @Autowired
    CustomizeAuthenticationAccessDenied customizeAuthenticationAccessDenied;
    @Autowired
    CustomizeAuthenticationLogout customizeAuthenticationLogout;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .csrf().disable()
                .anonymous().disable()
                .cors().and().httpBasic()
                .and()
                // 登录成功页面与登录失败页面
                .formLogin()
                .successHandler(customizeAuthenticationSuccessHandler).failureHandler(customizeAuthenticationFailHandler).permitAll().and()
                // 权限不足,即403时跳转页面
                .exceptionHandling().accessDeniedHandler(customizeAuthenticationAccessDenied).authenticationEntryPoint(new UnauthorizedEntryPoint())
                .and().logout().logoutSuccessHandler(customizeAuthenticationLogout).permitAll().and()
                .authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll()
                // 无需权限 即可访问
                .antMatchers("/logout").permitAll()
                // 需要USER角色才可访问
                .antMatchers("/person/**").hasRole("PERSON")
                // 需要ADMIN角色才可访问
                .antMatchers("/user/**").hasRole("ADMIN");
    }


    /**
     * 项目中重写的 UserDetailsService接口的实现类,需指定
     */
    @Qualifier("userService")
    @Autowired
    private UserDetailsService userDetailsService;
    /**
     * 初始验证登录  从内存中取密码
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());

    }

}

  重写的登录成功处理器代码如下:

@Component
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private static final Logger logger = LoggerFactory.getLogger(CustomizeAuthenticationSuccessHandler.class);

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {

        logger.info("AT onAuthenticationSuccess(...) function!");

        WebAuthenticationDetails details = (WebAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails();
        logger.info("login--IP:"+details.getRemoteAddress());

        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication1 = context.getAuthentication();
        Object principal = authentication1.getPrincipal();
        Object principal1 = authentication.getPrincipal();

        String name = authentication.getName();
        logger.info("login--name:"+name+" principal:"+principal+" principal1:"+principal1);

        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.append(JSONObject.toJSONString(ResponseData.ok().putDataValue("user",principal)
                    .putDataValue("name",name)));
        } catch (IOException e){
            e.printStackTrace();
        }finally {
            if (out != null) {
                out.close();
            }
        }
    }
}

以上是关于SpringBoot2.0.3 + SpringSecurity5.0.6 + vue 前后端分离认证授权的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot2.0.3 + SpringSecurity5.0.6 + vue 前后端分离认证授权

springboot2.0.3的spring版本是多少

springboot2.0.3使用jpa和hibernate的方式配置多数据源(sqlite和mysql)

送书25 本《SpringBoot2实战之旅》包邮免费送

如何使用 SpringBoot2、JUnit5 和 Kotlin 将配置属性注入单元测试

Micronaut 中的 Springs 资源是不是有等价物?