设置Spring Security配置类
1、设置包括认证、授权方法
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
List<String> securityIgnoreUrls = systemConfig.getSecurityIgnoreUrls();
String[] ignores = new String[securityIgnoreUrls.size()];
http
.addFilterAt(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
// 将自定义的认证过滤器添加到 UsernamePasswordAuthenticationFilter 位置
.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint)
// 配置认证失败处理器
.and().authenticationProvider(restAuthenticationProvider)
.authorizeRequests()
// 允许忽略安全检查的 URL 列表中的所有 URL
.antMatchers(securityIgnoreUrls.toArray(ignores)).permitAll()
// 只允许具有 ADMIN 角色的用户访问 /api/admin/** URL
.antMatchers("/api/admin/**").hasRole(RoleEnum.ADMIN.getName())
// 只允许具有 STUDENT 角色的用户访问 /api/student/** URL
.antMatchers("/api/student/**").hasRole(RoleEnum.STUDENT.getName())
.anyRequest().permitAll()
// 配置访问拒绝处理器
.and().exceptionHandling().accessDeniedHandler(restAccessDeniedHandler)
// 配置登录成功和失败处理器
.and().formLogin().successHandler(restAuthenticationSuccessHandler).failureHandler(restAuthenticationFailureHandler)
// 配置登出 URL 和登出成功处理器
.and().logout().logoutUrl("/api/user/logout").logoutSuccessHandler(restLogoutSuccessHandler).invalidateHttpSession(true)
// 配置 "记住我" 功能
.and().rememberMe().key(CookieConfig.getName()).tokenValiditySeconds(CookieConfig.getInterval()).userDetailsService(formDetailsService)
.and().csrf().disable()
.cors();
}
2、设置跨域资源共享
//设置跨域资源共享
@Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setMaxAge(3600L);
configuration.setAllowedOrigins(Collections.singletonList("*"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
}
3、自定义认证过滤器
@Bean
public RestLoginAuthenticationFilter authenticationFilter() throws Exception {
RestLoginAuthenticationFilter authenticationFilter = new RestLoginAuthenticationFilter();
authenticationFilter.setAuthenticationSuccessHandler(restAuthenticationSuccessHandler);
authenticationFilter.setAuthenticationFailureHandler(restAuthenticationFailureHandler);
authenticationFilter.setAuthenticationManager(authenticationManagerBean());
authenticationFilter.setUserDetailsService(formDetailsService);
return authenticationFilter;
}
二、关于spring security执行逻辑
Filter
public class RestLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final org.slf4j.Logger logger = LoggerFactory.getLogger(RestLoginAuthenticationFilter.class);
//设置该过滤器匹配的 URL 和 HTTP 方法,这里是匹配 /api/user/login 和 POST 请求。
public RestLoginAuthenticationFilter() {
super(new AntPathRequestMatcher("/api/user/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
UsernamePasswordAuthenticationToken authRequest;
try (InputStream is = request.getInputStream()) {
AuthenticationBean authenticationBean = JsonUtil.toJsonObject(is, AuthenticationBean.class);
request.setAttribute(TokenBasedRememberMeServices.DEFAULT_PARAMETER, authenticationBean.isRemember());
authRequest = new UsernamePasswordAuthenticationToken(authenticationBean.getUserName(), authenticationBean.getPassword());
} catch (IOException e) {
logger.error(e.getMessage(), e);
authRequest = new UsernamePasswordAuthenticationToken("", "");
}
setDetails(request, authRequest);
//return this.getAuthenticationManager().authenticate(authRequest); 这行代码的作用是将包含认证信息的
// Authentication 对象交给 Spring Security 的 AuthenticationManager 进行处理。AuthenticationManager 会根据配置的
// AuthenticationProvider 执行认证逻辑,并返回认证结果。
return this.getAuthenticationManager().authenticate(authRequest);
}
//配置记住我的服务
void setUserDetailsService(UserDetailsService userDetailsService) {
RestTokenBasedRememberMeServices tokenBasedRememberMeServices = new RestTokenBasedRememberMeServices(CookieConfig.getName(), userDetailsService);
tokenBasedRememberMeServices.setTokenValiditySeconds(CookieConfig.getInterval());
setRememberMeServices(tokenBasedRememberMeServices);
}
private void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}
provider
@Component
public class RestAuthenticationProvider implements AuthenticationProvider {
private final AuthenticationService authenticationService;
private final UserService userService;
private final WebContext webContext;
@Autowired
public RestAuthenticationProvider(AuthenticationService authenticationService, UserService userService, WebContext webContext) {
this.authenticationService = authenticationService;
this.userService = userService;
this.webContext = webContext;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
com.mindskip.xzs.domain.User user = userService.getUserByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("用户名或密码错误");
}
boolean result = authenticationService.authUser(user, username, password);
if (!result) {
throw new BadCredentialsException("用户名或密码错误");
}
UserStatusEnum userStatusEnum = UserStatusEnum.fromCode(user.getStatus());
if (UserStatusEnum.Disable == userStatusEnum) {
throw new LockedException("用户被禁用");
}
ArrayList<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority(RoleEnum.fromCode(user.getRole()).getRoleName()));
User authUser = new User(user.getUserName(), user.getPassword(), grantedAuthorities);
return new UsernamePasswordAuthenticationToken(authUser, authUser.getPassword(), authUser.getAuthorities());
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}