问题描述
于学期末有一个大实验,要求使用Java
相关技术,计划采用AngularJS
与SpringBoot
开发。
为了提高开发效率,特地在后台启用Spring Data REST
,少写了很多增删改查,不得不感叹SpringBoot
是真的厉害,只在配置文件中加入以下的配置,然后就在配置的url
上生成了相应实体的增删改查、分页排序接口。
spring:
data:
rest:
base-path: /data
除了基础的代码之外,登录也是一大问题,前后台分离如何进行用户认证呢?
用户认证
学习了潘老师的教程之后,对Spring
的session
管理也是有了一定了解。
HTTP
HTTP is stateless: But while the core of HTTP itself is stateless, HTTP cookies allow the use of stateful sessions. Using header extensibility, HTTP Cookies are added to the workflow, allowing session creation on each HTTP request to share the same context, or the same state.
HTTP
是无状态的,虽然HTTP
自身是无状态的,但是cookie
允许有状态session
的使用。通过使用Header
的扩展,cookie
被添加到工作流中,这些cookie
允许在每一个HTTP
请求创建会话,去共享上下文,或共享状态。
浏览器有cookie
,服务器有session
。
Session
在SpringBoot
中,我们使用依赖注入自动装配HttpSession
,用于管理session
。
但是,Spring
的所有组件,包括Controller
、Service
、Component
默认都是单例模式,那当多用户请求时,是如何保证这个session
,就是当前用户的session
呢?
Servlet
知其然,知其所以然。
框架只是我们提升开发效率的一种方式,没了框架,我们一样可以写,只是会造重复的轮子。
在Java
进入web
领域时,所有的开发都是基于Servlet
接口的,而我们常说的Tomcat
是一个Servlet
容器。
因为通过HTTP
协议进行数据通信,所以常用的是HttpServlet
抽象类。
在所有的会话跟踪技术中,HttpSession
对象是最强大和最通用的。
HttpSession
对象在用户第一次访问网站的时候自动被创建,可以通过调用HttpServletRequest
的getSession
方法获取该对象。
HttpSession getSession();
可以通过HttpSession
的setAttribute
方法将值放入HttpSession
,该值存储在内存中,所以不要往HttpSession
中放入太多数据。
void setAttribute(String name, Object value);
login
所以,最原始的写法就像下面这样,从HttpServletRequest
中获取HttpSession
,并设置属性。
request.getSession.setAttribute(CommonService.UserId, user.getId());
在Apache Tomcat
的官方文档中找到了下面一句话。
public interface HttpSessionProvides a way to identify a user across more than one page request or visit to a Web site and to store information about that user.
公共接口 HttpSession
提供一种认证跨页面或浏览
Web
站点的用户并存储用户相关信息的方式。
所以,我猜想,从cookie
到session
的绑定,应该是Tomcat
为我们处理的。
如果请求中没有cookie
,在getSession
时创建一个新的session
给我。
如果请求中有cookie
,在getSession
时查询已有的session
,然后把当前cookie
对应的session
给我。
SpringMVC
在SpringMVC
中,请求的分发是通过org.springframework.web.servlet.DispatcherServlet
完成的。
[dɪˈspætʃə(r)] ['sɜ:vlet]
Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception handling facilities.
HTTP
请求的前端控制器,为了web ui
控制器或者基于HTTP
的其他服务,它为一个web
请求注册handler
,提供了方便的映射与异常处理工具。
因为控制器的方法是被DispatcherServlet
所调度的,DispatcherServlet
持有请求与响应对象。
所以可以直接在控制器中注入相关对象。
可以直接注入HttpServletRequest
、HttpSession
等对象。
类似下面:
@PostMapping("/test")
public void test(HttpServletRequest request, HttpSession session) {
}
当然,因为request
中就有session
,所以也可以在request
中获取session
。
以上方法肯定没问题,直接在参数列表中注入,肯定线程安全。
如果改成如下写法,它还是线程安全的吗?
@Autowired
private HttpServletRequest request;
@Autowired
private HttpSession session;
@PostMapping("/test")
public void test() {
}
Spring 中获取 request 的几种方法,及其线程安全性分析
经测试,是的。Controller
是单例,但是Controller
是线程安全的。
因为Spring
容器在启动的时候,创建Controller
实例,但是HttpServletRequest
和HttpSession
注入的是代理对象,每次请求通过代理对象获取当前请求的request
或session
。
所以单例Controller
能保证不同请求有不同的session
,并且是线程安全的。
官方是更加推荐使用@Autowired
注入HttpSession
,因为这样注入的方式不局限于控制器内,如果Service
中也想用当前的session
,也能注入进来。
附加
记得之前对实体中某个属性设置not null
,发现有@Column(nullable = false)
和 @NotNull
两种方法。
今天在看《SpringMVC
初学指南时》明白了第二种写法。
第一种是Hibernate
中的,是在插入数据库时进行验证。
第二种是SpringMVC
支持的JSR
(Java
规范请求,Java Specification Requests
)中的,用于对控制器中接收的对象进行验证。
所以实际符合需求的是@Column(nullable = false)
。
@PostMapping("/test")
public void test(@Valid @RequestBody Teacher teacher) {
}
JSR
规范约束表
属性 | 描述 |
---|---|
@AssertFalse |
boolean 属性必须为false
|
@AssertTrue |
boolean 属性必须为true
|
@DecimalMax |
该属性值必须小于等于指定的小数 |
@DecimalMin |
该属性值必须大于等于指定的小数 |
@Digits |
应用于BigDecimal ,约束最大整数与最大小数。 |
@Future |
必须是未来的一个日期 |
@Past |
必须是过去的一个日期 |
@Max |
最大值 |
@Min |
最小值 |
@NotNull |
不为Null
|
@Null |
必须为Null
|
@Pattern |
必须匹配该正则表达式 |
@Size |
必须在指定范围内 |
总结
书籍,举世之宝!
感谢河北工业大学的图书馆,有各种于开发有益的书籍,受益良多。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。