SpringBoot2.0.3 + SpringSecurity5.0.6 + vue 先後端分離認證受權

  新項目引入安全控制

  項目中新近添加了Spring Security安全組件,前期沒怎麼用過,加之新版本少有參考,踩坑四天,終完成初步解決方案.其實很簡單,Spring Security5相比以前版本少了許多配置,操做起來更輕量前端

  MariaDb登陸配置加密策略

  SpringSecurity5在執行登陸認證時,需預設加密策略.ajax

  坑一:加密策略配置,驗密始終不經過,報錯401後端

  坑二:本地重寫的UserDetailsService實現類在注入的時候找不到,目前圖省事直接用了 @Qualifier制定跨域

  其它,實體類user實現UserDetails,role實現GrantedAuthority與以前版本並有太大變更,可參考不少,不作贅述安全

  代碼以下:  app

  /**
     * 項目中重寫的 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處理跨域的時候特別簡單,只須要在cors

  @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();
            }
        }
    }
}
相關文章
相關標籤/搜索