转载自:https://www.nbucedog.com/blog/article/8
叙述
当Spring security认证成功了之后会返回一个authentication,里面包含一些用户的认证信息。但有时候里面已有的信息并不能满足我们的需求,比如说当我们使用org.springframework.security.core.userdetails.User这个类的时候
代码
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException{
User user = userDAO.findByUsername(s);
if(user==null){
throw new UsernameNotFoundException("用户不存在");
}
Collection<GrantedAuthority> authorities = new ArrayList<>();
for (Role role:user.getRoleSet()){
for (Permission permission:role.getPermissionSet())
authorities.add(new SimpleGrantedAuthority(permission.getPermission()));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),authorities);
}
会得到下面这样的authentication
{
"authorities": [
{
"authority": "add_admin"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": null
},
"authenticated": true,
"principal": {
"password": null,
"username": "nbucedog",
"authorities": [
{
"authority": "add_admin"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "nbucedog"
}
可以看到这里关于用户的信息只有一个username,但其实我还想要用户的id,因为使用Spring注解进行权限认证时id会比username更保险(这个我后面会说)。
接下来我们看看怎么能够在authentication.principal里加入id吧
第一步:写一个实现UserDetails接口的类
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
public class SecurityUser extends User{
private Integer id;
public SecurityUser(String username, String password, Collection<GrantedAuthority> authorities) throws IllegalArgumentException{
super(username,password,authorities);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
因为org.springframework.security.core.userdetails.User本身就是UserDetails的实现类,而且它里面有许多东西我们还要用,所以我们就直接继承它并加入自己的东西,这里加入一个id属性
第二步:在loadUserByUsername方法里使用这个自定义的类
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException{
System.out.println("UserService:"+s);
User user = userDAO.findByUsername(s);
if(user==null){
throw new UsernameNotFoundException("用户不存在");
}
Collection<GrantedAuthority> authorities = new ArrayList<>();
for (Role role:user.getRoleSet()){
for (Permission permission:role.getPermissionSet())
authorities.add(new SimpleGrantedAuthority(permission.getPermission()));
}
SecurityUser securityUser = new SecurityUser(user.getUsername(),user.getPassword(),authorities);
securityUser.setId(user.getId());
return securityUser;
}
这样之后,我们会得到一个authentication.principal里包含id的authentication
{
"authorities": [
{
"authority": "add_admin"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": null
},
"authenticated": true,
"principal": {
"password": null,
"username": "nbucedog",
"authorities": [
{
"authority": "add_admin"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true,
"id": 1
},
"credentials": null,
"name": "nbucedog"
}
接下来我们就可以使用Spring注解进行权限配置了,比如要求只有用户自己能修改自己的用户信息
@RequestMapping(value = "/user",method = RequestMethod.PUT)
@ResponseBody
@PreAuthorize("#user.id==authentication.principal.id")
public Map UpdateUser(@RequestBody User user){
try {
User oldUser = userService.findById(user.getId());
oldUser.setNickname(user.getNickname());
oldUser.setSex(user.getSex());
oldUser.setPhone(user.getPhone());
oldUser.setMail(user.getMail());
oldUser.setResume(user.getResume());
userService.save(oldUser);
return ResultTools.result(0);
}catch (Exception e){
e.printStackTrace();
return ResultTools.dataResult(1000,e.getMessage());
}
}
接下来说说在代码里我们为什么使用了authentication.principal.id,而非authentication.principal.username
这里举个例子,假如我们使用了@PreAuthorize("#user.username==authentication.principal.username"),而某个用户名为badguys,id为2的bad guys提交了如下的json数据
{
"id":1,
"username":"badguys",
"nickname":"bad guys"
...
}
当id为2的bad guys用他自己注册的账户登录后,通过这个数据成功的通过了@PreAuthorize里的过滤条件,并且成功修改了id为1的用户信息。而当我们使用@PreAuthorize("#user.id==authentication.principal.id")时,这条带有攻击性的json数据则会被拦截。