0%

Spring Security——统一返回结果、自定义全局异常处理验证码验证

Spring Security——统一返回结果、自定义全局异常处理验证码验证

统一返回结果

  • 在自定义处理器之前,要统一一下我们的返回结果,一般都是以返回码加返回信息的结构来组织,这就像默认的返回信息200 OK,201 Created,400 Bad Request一样。
1
2
3
4
5
6
//自定义返回信息格式
public interface CustomizeResultCode {
Integer getCode();

String getMessage();
}
  • Result:获取返回信息的一个类,也可以用该类自定义返回信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Data
public class Result {

@ApiModelProperty(value = "是否请求成功")
private Boolean success;

@ApiModelProperty(value = "状态码")
private Integer code;

@ApiModelProperty(value = "响应携带的消息")
private String message;

@ApiModelProperty(value = "响应体的数据")
private Map<String, Object> data = new HashMap<>();

private Result() {

}

public static Result ok() {
Result result = new Result();
result.setSuccess(true);
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(ResultCode.OK.getMessage());
return result;
}

public static Result error() {
Result result = new Result();
result.setSuccess(false);
result.setCode(ResultCode.ERROR.getCode());
result.setMessage(ResultCode.BAD_REQUEST.getMessage());
return result;
}

/**
* 返回是否成功
*
* @param success
* @return
*/
public Result success(Boolean success) {
this.setSuccess(success);
return this;
}

public Result message(String message) {
this.setMessage(message);
return this;
}

public Result code(ResultCode code) {
this.setCode(code.getCode());
this.setMessage(code.getMessage());
return this;
}

public Result data(String key, Object value) {
this.data.put(key, value);
return this;
}

public Result data(Map<String, Object> map) {
this.setData(map);
return this;
}
}
  • ResultCode:返回信息,是一个枚举,里面限定了一些返回信息,可以直接使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.muchlab.response;

public enum ResultCode implements CustomizeResultCode {
/**
* 100:成功
*/
SUCCESS(100, "成功"),
/**
* 101:失败
*/
ERROR(101, "失败"),
//可自定义
LOGIN_FAIL(604, "登录失败");
private Integer code;
private String message;

ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}

@Override
public Integer getCode() {
return code;
}

@Override
public String getMessage() {
return message;
}
}

自定义全局异常处理

  • 配合统一的返回结果,可以自定义一个全局异常处理,来throw一些异常情况。
1
2
3
4
5
6
7
8
9
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CustomizeException extends RuntimeException {
@ApiModelProperty("状态码")
private ResultCode code;
@ApiModelProperty("错误信息")
private String errMsg;
}
  • @RestControllerAdvice表示拦截异常并统一处理,Rest表示返回结果为json数据,@ExceptionHandler:表示拦截哪种异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestControllerAdvice
public class GlobalExceptionHandler {

/**
* 捕获运行时异常,返回系统异常500
*
* @param e
* @return
*/
@ExceptionHandler(RuntimeException.class)
public Result error(RuntimeException e) {
e.printStackTrace();
return Result.error().code(ResultCode.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(CustomizeException.class)
public Result error(CustomizeException e) {
e.printStackTrace();

return Result.error()
.code(e.getCode())
.message(e.getErrMsg());
}
}

验证码验证

  • 首先,需要添加生成验证码的依赖
1
2
3
4
5
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
  • 创建验证码控制器,即生成验证码并将验证码存储到redis中,调用该url将会返回一个验证码图。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@Api(value = "验证码", tags = "验证码")
public class CaptchaController {
@Autowired
private RedisUtil redisUtil;

@GetMapping("/captcha")
@ApiOperation("获取验证码")
public Result captcha() {
GifCaptcha specCaptcha = new GifCaptcha(130, 48, 5);
String code = specCaptcha.text().toLowerCase();
String key = UUID.randomUUID().toString();
//存入redis中并设置过期时间为30秒
redisUtil.setEx(key, code, 30, TimeUnit.MINUTES);
final HashMap<String, Object> map = new HashMap<>();
map.put("key", key);
map.put("image", specCaptcha.toBase64());
return Result.ok().code(ResultCode.OK).data(map);
}
}
  • 创建认证验证码的过滤器VerifyCodeFilter,该类需要继承自GenericFilterBean,然后重写该类的doFilter方法,该类可以是任何过滤器的超类。配置参数的类型转换是自动的,将使用转换后的值调用相应的setter方法。
  • 其中verCode是根据上面控制器返回的验证码图进行填写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Component
public class VerifyCodeFilter extends GenericFilterBean {
//登录的路径
private String defaultFilterProcessUrl = "/login";
@Autowired
private RedisUtil redisUtil;

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
if ("POST".equalsIgnoreCase(request.getMethod()) &&
defaultFilterProcessUrl.equals(request.getServletPath())) {
//当登录的时候获取参数中的验证码值和验证码的key
String verCode = request.getParameter("verCode");
String verKey = request.getParameter("verKey");
//然后用key去redis中取code,进行校验
String redisCode = redisUtil.get(verKey);
Result result = Result.error();
if (verCode == null) {
verCodeReturn(response, result, "验证码不能为空!");
return;
}
if (redisCode == null) {
verCodeReturn(response, result, "不存在该验证码,请刷新验证码!");
return;
}
if (!redisCode.equals(verCode.trim().toLowerCase())) {
verCodeReturn(response, result, "验证码错误!");
return;
}
}
chain.doFilter(request, response);
}

private void verCodeReturn(HttpServletResponse response, Result result, String msg) throws IOException {
result.message(msg);
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(JSON.toJSONString(result));
out.flush();
out.close();
}
}
-------------本文结束感谢您的阅读-------------