4.4 了解用户是谁
4.4 了解用户是谁
通常,仅仅知道用户已登录是不够的,我们一般还需要知道他们是谁,这样才能优化体验。
例如,在OrderController中,在最初创建Order的时候会绑定一个订单的表单,如果我们能够预先将用户的姓名和地址填充到Order中就好了,这样用户就不需要为每个订单都重新输入这些信息了。也许更重要的是,在保存订单的时候应该将Order实体与创建该订单的用户关联起来。
为了在Order实体和User实体之间实现所需的关联,我们需要为Order类添加一个新的属性:
1 |
|
这个属性上的@ManyToOne
注解表明一个订单只能属于一个用户,但是,一个用户却可以有多个订单。因为我们使用了Lombok,所以不需要为该属性显式定义访问器方法。
在OrderController中,processOrder()方法负责保存订单。这个方法需要修改以便于确定当前的认证用户是谁,并且要调用Order对象的setUser()方法来建立订单和用户之间的关联。
我们有多种方式确定用户是谁,常用的方式如下:
- 注入Principal对象到控制器方法中;
- 注入Authentication对象到控制器方法中;
- 使用SecurityContextHolder来获取安全上下文;
- 使用
@AuthenticationPrincipal
注解来标注方法。
举例来说,我们可以修改processOrder()方法,让它接受一个java.security.Principal类型的参数。然后,我们就可以使用Principal的名称从UserRepository中查找用户了:
1 |
|
这种方式能够正常运行,但是它会在与安全无关的功能中掺杂安全性的代码。我们可以修改processOrder()方法,让它不再接受Principal参数,而是接受Authentication对象作为参数:
1 |
|
有了Authentication对象之后,我们就可以调用getPrincipal()来获取principal对象,在本例中,也就是一个User对象。需要注意,getPrincipal()返回的是java.util.Object,所以我们需要将其转换成User。
最整洁的方案可能是在processOrder()中直接接受一个User对象,不过我们需要为其添加@AuthenticationPrincipal
注解,这样它才会变成认证的principal:
1 |
|
@AuthenticationPrincipal
非常好的一点在于它不需要类型转换(前文中的Authentication则需要进行类型转换),同时能够将安全相关的代码仅仅局限于注解本身。在processOrder()得到User对象之后,我们就可以使用它并赋值给Order了。
还有另外一种方式能够识别当前认证用户是谁,但是这种方式有点麻烦,它会包含大量安全性相关的代码。我们可以从安全上下文中获取一个Authentication对象,然后像下面这样获取它的principal:
1 | Authentication authentication = |
尽管这个代码片段充满了安全性相关的代码,但是它与前文所述的其他方法相比有一个优势:它可以在应用程序的任何地方使用,而不仅仅是在控制器的处理器方法中。这使得它非常适合在较低级别的代码中使用。