poml.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
KaptchaConfig
@Configuration
public class KaptchaConfig {
@Bean
public Producer kaptcha() {
Properties properties = new Properties();
properties.setProperty("kaptcha.image.width", "150");
properties.setProperty("kaptcha.image.height", "50");
properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
properties.setProperty("kaptcha.textproducer.char.length", "4");
Config config = new Config(properties);
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
VerifyCodeController
@RestController
public class VerifyCodeController {
private final Producer producer;
@Autowired
public VerifyCodeController(Producer producer) {
this.producer = producer;
}
@GetMapping("/vc.jpg")
public String getVerifyCode(HttpSession session) throws IOException {
//1.生成验证码
String text = producer.createText();
//2.放入 session redis 实现
session.setAttribute("kaptcha", text);
//3.生成图片
BufferedImage bi = producer.createImage(text);
FastByteArrayOutputStream fos = new FastByteArrayOutputStream();
ImageIO.write(bi, "jpg", fos);
//4.返回 base64
return Base64.encodeBase64String(fos.toByteArray());
}
}
SecurityConfig
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//自定义内存数据源
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("admin").build());
return inMemoryUserDetailsManager;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//配置
@Bean
public LoginKaptchaFilter loginKaptchaFilter() throws Exception {
LoginKaptchaFilter loginKaptchaFilter = new LoginKaptchaFilter();
//1.认证 url
loginKaptchaFilter.setFilterProcessesUrl("/doLogin");
//2.认证 接收参数
loginKaptchaFilter.setUsernameParameter("uname");
loginKaptchaFilter.setPasswordParameter("passwd");
loginKaptchaFilter.setKaptchaParameter("kaptcha");
//3.指定认证管理器
loginKaptchaFilter.setAuthenticationManager(authenticationManagerBean());
//4.指定成功时处理
loginKaptchaFilter.setAuthenticationSuccessHandler((req, resp, authentication) -> {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "登录成功");
result.put("用户信息", authentication.getPrincipal());
resp.setContentType("application/json;charset=UTF-8");
resp.setStatus(HttpStatus.OK.value());
String s = new ObjectMapper().writeValueAsString(result);
resp.getWriter().println(s);
});
//5.认证失败处理
loginKaptchaFilter.setAuthenticationFailureHandler((req, resp, ex) -> {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "登录失败: " + ex.getMessage());
resp.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
resp.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
resp.getWriter().println(s);
});
return loginKaptchaFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/vc.jpg").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.exceptionHandling()
.authenticationEntryPoint((req, resp, ex) -> {
resp.setContentType("application/json;charset=UTF-8");
resp.setStatus(HttpStatus.UNAUTHORIZED.value());
resp.getWriter().println("必须认证之后才能访问!");
})
.and()
.logout()
.and()
.csrf().disable();
http.addFilterAt(loginKaptchaFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
KaptchaNotMatchException
//自定义验证码认证异常
public class KaptchaNotMatchException extends AuthenticationException {
public KaptchaNotMatchException(String msg) {
super(msg);
}
public KaptchaNotMatchException(String msg, Throwable cause) {
super(msg, cause);
}
}
LoginKaptchaFilter
//自定义 filter
public class LoginKaptchaFilter extends UsernamePasswordAuthenticationFilter {
public static final String FORM_KAPTCHA_KEY = "kaptcha";
private String kaptchaParameter = FORM_KAPTCHA_KEY;
public String getKaptchaParameter() {
return kaptchaParameter;
}
public void setKaptchaParameter(String kaptchaParameter) {
this.kaptchaParameter = kaptchaParameter;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
try {
//1.获取请求数据
Map<String, String> userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);
String kaptcha = userInfo.get(getKaptchaParameter());//用来获取数据中验证码
String username = userInfo.get(getUsernameParameter());//用来接收用户名
String password = userInfo.get(getPasswordParameter());//用来接收密码
//2.获取 session 中验证码
String sessionVerifyCode = (String) request.getSession().getAttribute("kaptcha");
if (!ObjectUtils.isEmpty(kaptcha) && !ObjectUtils.isEmpty(sessionVerifyCode) &&
kaptcha.equalsIgnoreCase(sessionVerifyCode)) {
//3.获取用户名 和密码认证
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
} catch (IOException e) {
e.printStackTrace();
}
throw new KaptchaNotMatchException("验证码不匹配!");
}
}