Spring Security里的authentication中加入自定义的内容

mac2026-08-09  4

转载自: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数据则会被拦截。

最新回复(0)