新项目引入安全控制
项目中新近添加了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()); }}
重写的登录成功处理器代码如下:
@Componentpublic 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(); } } }}