spring security 是基于filter 方式进行拦截。登录拦截器是UsernamePasswordAuthenticationFilter类,
查看过滤器源码:
UsernamePasswordAuthenticationFilter 仅仅采用requeste.getParameter 方法进行了参数获取。对json 方式并不支持。
解决办法:
重写 UsernamePasswordAuthenticationFilter 过滤器:
```
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
public class CustomAuthenticationFilterextends UsernamePasswordAuthenticationFilter {
@Override
public AuthenticationattemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException {
//attempt Authentication when Content-Type is json
if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)
|| request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
//use jackson to deserialize json
ObjectMapper mapper =new ObjectMapper();
UsernamePasswordAuthenticationToken authRequest =null;
try (InputStream is = request.getInputStream()) {
AuthenticationBean authenticationBean = mapper.readValue(is, AuthenticationBean.class);
authRequest =new UsernamePasswordAuthenticationToken(
authenticationBean.getUsername(), authenticationBean.getPassword());
}catch (IOException e) {
e.printStackTrace();
authRequest =new UsernamePasswordAuthenticationToken(
"", "");
}finally {
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
//transmit it to UsernamePasswordAuthenticationFilter
else {
return super.attemptAuthentication(request, response);
}
}
}
```
自定义实体Bean:
```
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AuthenticationBean {
private Stringusername;
private Stringpassword;
}
```
最后一步 也是最重要的
配置 spring security
```
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled =true)
public class SecurityConfigextends WebSecurityConfigurerAdapter {
/**
* 主配置方法
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http)throws Exception {
// fixme: 生产环境应参照官方方式打开csrf防护
http.authorizeRequests()
.antMatchers("/rest/system/login", "/swagger-ui.html/**", "/webjars/**", "/v2/api-docs", "/swagger-resources/**").permitAll()
.antMatchers("/rest/**").hasAuthority("admin")
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(accessDeniedHandler()).authenticationEntryPoint(authenticationEntryPointHandler())
// .and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and().formLogin().loginPage("/")
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().cors()
.and().csrf().disable();
// http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterAt(customAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
private CustomAuthenticationFiltercustomAuthenticationFilter()throws Exception {
CustomAuthenticationFilter filter =new CustomAuthenticationFilter();
filter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
filter.setAuthenticationFailureHandler(authenticationFailureHandler());
filter.setFilterProcessesUrl("/rest/system/login");
//这句很关键,重用WebSecurityConfigurerAdapter配置的AuthenticationManager,不然要自己组装AuthenticationManager
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception {
auth.userDetailsService(userDetailsServiceImpl())
.passwordEncoder(passwordEncoder());
}
/**
* 自定义登录检验方法
*
* @return
*/
@Bean
protected MyUserDetailsServiceuserDetailsServiceImpl() {
return new MyUserDetailsService();
}
/**
* 自定义加密方式
*
* @return
*/
@Bean
public PasswordEncoderpasswordEncoder() {
// 自定义加密方式
// return new MyPasswordEncoder();
return new BCryptPasswordEncoder();
}
/**
* 自定义登录成功处理Bean
*
* @return
*/
@Bean
public MyAuthenticationSuccessHandlerauthenticationSuccessHandler() {
return new MyAuthenticationSuccessHandler();
}
/**
* 自定义登录失败处理Bean
*
* @return
*/
@Bean
public MyAuthenticationFailureHandlerauthenticationFailureHandler() {
return new MyAuthenticationFailureHandler();
}
/**
* 自定义登录检验过滤器
*
* @return
* @throws Exception
*/
@Bean
public MyAuthenticationTokenFilterauthenticationTokenFilter()throws Exception {
MyAuthenticationTokenFilter authenticationTokenFilter =new MyAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManager());
return authenticationTokenFilter;
}
/**
* 自定义401
*
* @return
*/
@Bean
public MyAuthenticationEntryPointHandlerauthenticationEntryPointHandler() {
return new MyAuthenticationEntryPointHandler();
}
/**
* 自定义403
*
* @return
*/
@Bean
public MyAccessDeniedHandleraccessDeniedHandler() {
return new MyAccessDeniedHandler();
}
}
```