spring security
自定义处理登陆
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
项目结构
配置类
/**
* @author spp
* @date 2020-06-11 14:17
**/
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
final
AuthUserDetailsServiceImpl userDetails;
final
AuthSuccessHandler authSuccessHandler;
final
AuthFailHandler authFailHandler;
public SecurityConfig(AuthSuccessHandler authSuccessHandler, AuthFailHandler authFailHandler, AuthUserDetailsServiceImpl userDetails) {
this.authSuccessHandler = authSuccessHandler;
this.authFailHandler = authFailHandler;
this.userDetails = userDetails;
}
@Override
public void configure(WebSecurity web) throws Exception {
//忽略请求,不经过security过滤器链
web.ignoring().mvcMatchers(HttpMethod.GET,"/**");
}
/**
* 从容器中取出 AuthenticationManagerBuilder,执行方法里面的逻辑之后,放回容器
* @param authenticationManagerBuilder x
* @throws Exception
*/
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetails).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
public void configure(HttpSecurity http) throws Exception {
//解决跨域问题。cors 预检请求放行,让Spring security 放行所有preflight request(cors 预检请求)
http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
//让Security永远不会创建HttpSession,它不会使用HttpSession来获取SecurityContext
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().headers().cacheControl();
http.authorizeRequests()
.antMatchers("/admin/**")
.hasAuthority("root")
.antMatchers("/").permitAll();
//登陆
http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
//处理异常情况:认证失败和权限不足
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.getWriter().println("认证失败");
}).accessDeniedHandler((request, response, accessDeniedException) -> {
response.getWriter().println("你的权限不足以访问该资源");
});
}
/**
* 登陆拦截器
* @return
* @throws Exception
*/
@Bean
public UsernamePassAuthFilter myUsernamePasswordAuthenticationFilter() throws Exception {
UsernamePassAuthFilter filter = new UsernamePassAuthFilter();
//成功后处理
filter.setAuthenticationSuccessHandler(authSuccessHandler);
//失败后处理
filter.setAuthenticationFailureHandler(authFailHandler);
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
}
自定义的登陆过滤器,继承UsernamePasswordAuthenticationFilter
/**
* @author spp
* @date 2020-06-09 15:34
* 自定义登陆
**/
public class UsernamePassAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
UserService service;
public static final String APPLICATION_JSON = "application/json";
public static final String APPLICATION_JSON_UTF8 = "application/json;charset=UTF-8";
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
String contentType = request.getContentType();
if (contentType == null){
throw new AuthException("内容类型不符");
}
if ( APPLICATION_JSON.equals(contentType) || APPLICATION_JSON_UTF8.equals(contentType)) {
UsernamePasswordAuthenticationToken authRequest;
User user;
try (InputStream is = request.getInputStream()) {
int len;
byte[] b = new byte[2048];
StringBuilder sb = new StringBuilder();
while ((len = is.read(b)) != -1){
sb.append(new String(b,0,len));
}
user = JSON.parseObject(sb.toString(),User.class);
} catch (IOException e) {
//将异常放到自定义的异常类中
throw new AuthException(e.getMessage());
}catch (JSONException j){
throw new AuthException("参数映射错误"+j.getMessage());
}
try {
if (user != null) {
//获得账号、密码
logger.info(user);
//登陆逻辑
if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())){
authRequest = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
setDetails(request, authRequest);
return getAuthenticationManager().authenticate(authRequest);
}else {
throw new AuthException("用户名密码不匹配");
}
}
} catch (Exception e) {
throw new AuthException(e.getMessage());
}
return null;
} else {
response.setStatus(405);
throw new AuthException("内容类型不符");
}
}
}
自定义登陆成功处理类,实现AuthenticationSuccessHandler接口
/**
* @author spp
* @date 2020-06-11 14:28
**/
@Slf4j
@Component
public class AuthSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException {
//取得账号信息
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
log.info(userDetails + "--->>登陆成功");
response.getWriter().println("success");
}
}
自定义登陆失败处理类,实现AuthenticationFailureHandler接口
/**
* @author spp
* @date 2020-06-09 16:50
**/
@Component
@Slf4j
public class AuthFailHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException {
log.info("登陆失败--->>>>");
//输出
JSONUtil.WriteJSON(request, response,"登陆失败:"+e.getMessage());
}
}
登陆成功后给其添加权限或者身份
实体类需要实现UserDetails接口
/**
* @author spp
* @date 2020-06-09 15:54
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AuthUser implements UserDetails {
private String username;
private String password;
private Integer state;
public AuthUser(String username, String password, Integer state, Collection<? extends GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.state = state;
this.authorities = authorities;
}
private Boolean rememberMe;
private Collection<? extends GrantedAuthority> authorities;
//账号是否未过期
@Override
public boolean isAccountNonExpired() {
return true;
}
//是否未锁定
@Override
public boolean isAccountNonLocked() {
return true;
}
//凭据是否未过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//是否启用
@Override
public boolean isEnabled() {
return true;
}
}
添加权限类,需要实现UserDetailsService接口
/**
* @author spp
* @date 2020-06-11 14:34
* 登陆成功后添加权限或者身份
**/
@Component
public class AuthUserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new AuthUser(username,new BCryptPasswordEncoder().encode("123456"),null);
}
}