2021SC@SDUSC
我们的项目中用到了很多关于Spring Security的内容,笔者虽然已经在学习Spring Security了,但是对于项目这种多个模块分离的Spring Security的使用流程还是不甚了解,今天我们就来理清一下项目中Spring Security 的逻辑。
JwtAuthenticationTokenFilter
首先我们看到位于security模块的 JwtAuthenticationTokenFilter
模块,他的 doFilterInternal 中关于当用户尚未认证时的处理逻辑为:
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
LOGGER.info("authenticated user: {}", username);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
这里可以看到他调用了 userDetailsService 的 loadUserByUsername 方法。但是笔者却发现 security 模块中并没有对应的 userDetailService 的实现类,于是笔者猜测,关于 userDetailService 的实现类应该是每一个需要 security 的模块自己实现的。
我们转到 doc 模块下。
EditorUserDetailsServiceImpl
可以看到这个模块中出现了对应的 UserDetailService 的实现类,我们观察他的 loadUserByUsername 方法的实现:
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UmsUser user = userRepository.findByUsername(username).get(0);
List<GrantedAuthority> authorityList = new ArrayList<>();
/// =========================
List<EditorRoleForUserVo> editorRoleForUserVos = authorityService.selectRoleForUser(user.getUid());
/// =========================
editorRoleForUserVos.forEach( role -> {
authorityList.add(new SimpleGrantedAuthority(role.getRole_name()));
});
return new EditorUserDetails(user, authorityList);
}
可以看到,这个方法中的关键操作是
List<EditorRoleForUserVo> editorRoleForUserVos = authorityService.selectRoleForUser(user.getUid());
调用了 authorityService 的 selectRoleForUser 方法
我们需要找到 authorityService 的实现类。
EditorAuthorityServiceImpl
这个实现类中只实现了一个方法,也就是 selectRoleForUser :
@Override
@Transactional
public List<EditorRoleForUserVo> selectRoleForUser(Integer userId) {
List<EditorRoleForUserVo> resultList = new ArrayList<>();
StringBuilder sql_select_role_for_user = new StringBuilder();
sql_select_role_for_user
.append("select ")
.append("u.uid uid, u.username username, r.rid rid, r.`name` role_name, r.description role_description ")
.append("from ")
.append("ums_user u ")
.append("left join ums_user_role_relation ur on u.uid = ur.uid ")
.append("left join ums_role r on ur.rid = r.rid ")
.append("where ")
.append("u.uid = ")
.append(userId);
Query query = entityManager.createNativeQuery(sql_select_role_for_user.toString());
List list = query.unwrap(NativeQueryImpl.class)
.setResultTransformer(Transformers.aliasToBean(EditorRoleForUserVo.class))
.getResultList();
list.forEach(item -> {
resultList.add((EditorRoleForUserVo) item);
});
return resultList;
}
可以看到这里面有一个sql语句,我们首先观察一下他用到数据库,分别是
- usm_user
- ums_user_relation
- ums_role
实际上这部操作可以得到user对应的role_name,并且返回一个list,表示这个user拥有的角色数。
查看了数据库后首先使用了正确的用户进行登录,获取到了token。
然后从JwtAuthenticationTokenFilter可得知,需要将这个token附到请求头中,并且需要将token前面加上一个特定的字符串。
进行了以上操作后,请求成功:
@PreAuthorize("hasAnyRole('administrator_editor', 'author', 'administrator')")
@RequestMapping(value = "/get_latest_id", method = RequestMethod.POST)
public CommonResult<Long> getLatestNewId() {
Long latestId = service.getLatestId() + 1;
return CommonResult.success(latestId);
}
{
"code": 200,
"message": "操作成功",
"data": 1
}
分析完毕