新項目引入安全控制
項目中新近添加了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();
}
}
}
}