一、Token
token是什么?
token是服务端生成的一个标识客户端的字符串,具有唯一性,作为访问服务端的凭证。特殊的 Token(如JWT)可以携带信息。
为什么要用 Token?
为了解决HTTP无状态的问题
无状态指每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况。
反之有状态就是请求之间可以建立关系,服务端能够区分请求是谁发送的请求。
为了使之有状态HTTP加入了 Cookie 和 Session 机制。
基于Session会话的缺点
Session是基于Cookie实现的。服务端保存用户信息存放到Session中,客户端在Cookie中存放 session id ,客户端每次发送在 HTTP Header 带上 session id,服务端从 HTTP Header 拿到 session id 后查对应的 session 以获取用户信息,得到用户信息服务端就可以对请求做拦截判断,对请求资源进行鉴权。
使用session进状态管理的缺点:
- 服务器宕机,保存的用户信息消失
- 重启服务,保存的用户信息消失
- 迫使客户端只能访问固定的服务器(其信息保存在某台服务器上)
Token 改进 Session 会话方式
- 改进了 Session 会话的诸多缺点
- 可以方便的提供第三方服务
二、Redis
Redis 数据非关系型内存数据库,常用来做缓存,处在出在DAO与数据库中间。
常用场景:
- 存储token
- 存储验证码
- 缓存页面显示数据,如菜单等首页信息
- 缓存数据库中不经常改变的信息
三、RESTful API 规范
RESTful 是目前最流行的 API 设计规范,用于 Web 数据接口的设计。
为什么要使用这套规范?
- 使接口易理解,易扩展
- 方便不同的前端设备与后端通信
1、协议 (Protocol)
接口之间的通信总是使用 HTTPS
HTTPS:在HTTP传输过程中使用公私钥对传输进行加密(TLS/SSL)
2、域名 (Domain)
作为第三方服务API的域名。
如果API很简单,推荐这种方式。
3、版本 (Version)
通常将版本号放在URL中。通常默认API为1版本v1
并不加,到2版本时加v2
。
4、路径 (Endpoint)
路径表示访问API的具体网址。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。
- https://api.example.com/v1/zoos
- https://api.example.com/v1/animals
- https://api.example.com/v1/employees
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。
5、HTTP动词
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面五个(括号里是对应的SQL命令)。
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
- PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
- DELETE(DELETE):从服务器删除资源。
不常用的HTTP动词。
- HEAD:获取资源的元数据。
- OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
示例:
- GET /zoos:列出所有动物园
- POST /zoos:新建一个动物园
- GET /zoos/ID:获取某个指定动物园的信息
- PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
- PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
- DELETE /zoos/ID:删除某个动物园
- GET /zoos/ID/animals:列出某个指定动物园的所有动物
- DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
推荐文章:
http://www.ruanyifeng.com/blog/2011/09/restful.html
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html
6、过滤信息(Filtering)
如果记录数量很多,服务器不可能都将它们返回给用户,通常用查询字符串过滤。
- ?page=2
- ?page=2&size=10
- ?key=liu&age=18
7、状态码(Status Codes)
没次客户端请求服务器都给出回应。回应即 HTTP 状态码和数据。
- 1xx:相关信息
- 2xx:操作成功
- 3xx:重定向
- 4xx:客户端错误
- 5xx:服务器错误
常见状态码
- 200:操作成功
- 301:永久重定向,API返回的都是json数据用不到这个状态码
- 302:暂时重定向
- 303:重定向,参考另一个 URL
- 400 Bad Request:服务器不理解客户端的请求,未做任何处理。
- 401 Unauthorized:用户未提供身份验证凭据,或者没有通过身份验证。
- 403 Forbidden:用户通过了身份验证,但是不具有访问资源所需的权限。
- 404 Not Found:所请求的资源不存在,或不可用。
- 500 Internal Server Error:客户端请求有效,服务器处理时发生了意外。
数据对象
DTO(Data Transfer Object)
这个传输通常指的前后端之间的传输
DTO是一个比较特殊的对象,他有两种存在形式:
- 在后端,他的存在形式是java对象,也就是在controller里面定义的那个东东,通常在后端不需要关心怎么从json转成java对象的,这个都是由一些成熟的框架帮你完成啦,比如spring框架
- 在前端,他的存在形式通常是js里面的对象(也可以简单理解成json),也就是通过ajax请求的那个数据体
ps:项目中 Service 和 Controller 层一般入参是DTO对象,返回参数是 BO 或者 BO 的集合(Collection)。
BO(Business Object):业务对象,组装多个entity(PO),一般会继承一个entity(PO)并对其扩展。BO 通常是在 service 方法组装完成的。
//entity
class User{
private String name;
private Integer age;
}
class UserBO extends User{
List<Dept> depts;
List<Role> roles;
}
VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来给前端使用。
推荐文章:https://zhuanlan.zhihu.com/p/102389552
跨域问题
跨域问题是由于浏览器的同源策略引起的,同源策略是浏览器安全措施的一种约定。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。
同源就是一个域,即协议、域名(主机)、端口号相同认为同源。出现任意一个不同的就认为不同源,即出现跨域。
出现跨域引起的问题
- 资源无法请求
- 无发送 AJAX 请求
- 无法读取 Cookie、LocalStorage 和 IndexedDB
解决跨域的方法
根本解决办法是通过 CORS(Cross-Origin Resource Sharing)解决,它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
JSONP方式
1、JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。
核心思想:网页通过添加一个script元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
<script src="http://test.com/data.php?callback=dosomething"></script>
// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
// 处理服务器返回回调函数的数据
<script type="text/javascript">
function dosomething(res){
// 处理获得的数据
console.log(res.data)
}
</script>
$.ajax({
url: 'http://www.test.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "handleCallback", // 自定义回调函数名
data: {}
});
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
后端处理
服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
2、带cookie跨域请求:前后端都需要进行设置
项目中 Spring Security 使用 CORS 处理跨域
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//1) *默认所有ip和端口都可以访问,也可以设置固定
config.addAllowedOrigin("*");
//2) 是否发送Cookie信息
config.setAllowCredentials(true);
//3) 允许的请求方式
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
// 4)允许的头信息
config.addAllowedHeader("*");
// 5)有效时长
config.setMaxAge(3600L);
//2.添加映射路径,我们拦截一切请求
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
//3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
普通处理跨域。
/*
* 导入包:import javax.servlet.http.HttpServletResponse;
* 接口参数中定义:HttpServletResponse response
*/
// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com");
// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true");
// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
水平越权,垂直越权
水平越权指权限相同的用户发生了相互操作。如:两个不同的管理部门(都对用户管理)一个部门的管理员可以管理另一个部门的员工;一个管理员可以把另一个管理员删除。
垂直越权指不同权限的用户发生相互操作(一般是低权限操作高权限的)。如:普通用户删除管理员。
对于这些问题,一般在service的业务层通过逻辑判断解决,例如:对于出现跨部门操作的水平越权问题可以在操作前判断管理员与被操作者是否属于同一部门解决。