前言
2021SC@SDUSC
概述
本篇博客继续上次的话题,对项目Controller层源码分析进行收尾
源码
源码如下:
@RestController
@RequestMapping("/problemset")
public class ProblemsetController {
@Autowired
private ProblemsetService problemsetService;
@Autowired
private ProblemService problemService;
private final Logger log = LoggerFactory.getLogger(ProblemController.class);
@PostMapping("/getProblemset")
@PreAuthorize("hasRole('ROLE_COMMON')")
public ResultEntity getProblemset(@RequestParam("id") int id) {
try {
Problemset problemset = problemsetService.getProblemset(id);
List<Problem> problemList = problemsetService.getProblemList(id);
problemset.setProblems(problemList);
return ResultEntity.success("题目集", problemset);
} catch (Exception e) {
log.error("查看题目集错误", e);
return ResultEntity.error("查看题目集错误,请重试");
}
}
}
在上一篇博客中已经分析了@Autowired实现原理,slf4j日志,@PostMapping实现原理等
下面我们将对剩余关键点进行分析,其实就是对:
1.@PreAuthorize("hasRole('ROLE_COMMON')")
2.@RequestParam("id") int id
3. return ResultEntity
三个部分进行分析
@PreAuthorize实现原理
@PreAuthorize注解来自于SpringSecurity,是SpringSecurity提供的权限安全认证注解。是在进入方法前进行权限验证,@PreAuthorize 声明这个方法所需要的权限表达式
使用方法
hasRole:
角色授权:授权代码,在我们返回的UserDetails的Authority需要加ROLE_前缀,Controller上使用时不要加前缀,例如:
hasAuthority:
权限授权:用户自定义的权限,返回的UserDetails的Authority只要与这里匹配就可以,这里不需要加ROLE_,名称保持一至即,例如:
其余使用不如上面两个频繁:
源代码
package org.springframework.security.access.prepost;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize {
String value();
}
分析原理
这里我们以hasAuthority()方法为示例,其余方法的原理大同小异:
原理就是:根据这个注解所需要的权限,再和当前登录的用户角色所拥有的权限对比,如果用户的角色权限集Set中有这个权限,则放行;没有,拒绝
所需要的权限由注解参数传递,登录角色拥有的权限由spring security控制,在用户login的时候已经获取并保存
详细代码流程:
@RequestParam实现原理
使用方法
@RequestParam可以将请求参数绑定到控制器的方法参数上,规定了方法所接受的参数列表
语法:@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)
value:参数名
required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值
例子:
源代码
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
分析原理
简单来说就是通过 request.getParameter() 方法去获取参数值,然后对 @RequestParam 中的属性进行比对,实现由注解到底层代码的封装
具体封装流程:
ResultEntity实现原理
使用方法
作为controller层返回参数的实体类,可以return resultEntity.success()或者return resultEntity.error()控制返回的类型,通过不同的参数列表实现不同的返回效果,例如:
return ResultEntity.success("公开题目集", problemsets);
return ResultEntity.error("查看公开题目集错误,请重试");
return ResultEntity.success("题目集添加成功");
等等
源代码
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
public class ResultEntity {
@JSONField(ordinal = 1)
private int code;
@JSONField(ordinal = 2)
private String message;
@JSONField(ordinal = 3)
private Object data;
public ResultEntity(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public static ResultEntity success() {
return new ResultEntity(0, "success", true);
}
public static ResultEntity success(String message) {
return new ResultEntity(0, message, null);
}
public static ResultEntity success(String message, Object data) {
return new ResultEntity(0, message, data);
}
public static ResultEntity data(Object data) {
return success("success", data);
}
public static ResultEntity data(){
return success("success");
}
public static ResultEntity error(String message) {
return new ResultEntity(-1, message, null);
}
public static ResultEntity error(String message, Object data) {
return new ResultEntity(-1, message, data);
}
public static ResultEntity error(int code, String message) {
return new ResultEntity(code, message, null);
}
public static ResultEntity error(StatusCode entity){
return new ResultEntity(entity.getCode(), entity.getMessage(), null);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
}
分析原理
内置code,message,data的实体类,通过不同的构造参数实现不同的数据封装,并作为返回体返回请求的结果
总结
controller层的源码分析就到此为止了,可以看出来一些注解和类的底层实现机制还是比较复杂的,了解其实现原理对我们提高代码分析和开发能力有一定帮助。
下一篇分析预计会对service层代码进行分析,感谢阅读。
参考资料
@RequestParam注解使用_一叶知秋-CSDN博客_@requestparam
Spring Security - @PreAuthorize安全表达式hasRole、hasAuthority区别_LeoSong121的博客-CSDN博客Spring Security @PreAuthorize 权限控制的原理_u010227042的博客-CSDN博客Spring Security - @PreAuthorize安全表达式hasRole、hasAuthority区别_LeoSong121的博客-CSDN博客