明确三个接口GrantedAuthority(角色权限) UserDetails (用户信息) UserDetailsService(获取用户信息和用户权限)
角色表DO实现GrantedAuthority接口:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RoleInfo implements GrantedAuthority{
private static final long serialVersionUID = 7088351652237386029L;
private Long roleId;
private String roleName;
private String roleDesc;
@Override
public String getAuthority() {
return roleName;
}
}
MyUserDetails实现UserDetails接口,里面包含用户信息和用户角色信息
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyUserDetails implements UserDetails {
private UserInfo userInfo;
private List<RoleInfo> roleList;
// private List<SimpleGrantedAuthority> authority;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//返回当前用户的权限
return roleList;
// return roleList.stream()
// .filter(role -> role.getRoleName()!=null)
// .map(role ->new SimpleGrantedAuthority(role.getRoleName()))
// .collect(Collectors.toList());
}
@Override
public String getPassword() {
return userInfo.getUserPassword();
}
@Override
public String getUsername() {
return userInfo.getUserName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
实现UserDetailService,重写loadUserByUsername方法用来获取UserDetail
@Component
public class UserDetailServiceImp implements UserDetailsService {
@Autowired
UserInfoService userInfoService;
@Autowired
UserRoleService userRoleService;
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
// 获取登录用户信息
UserInfo admin = userInfoService.selectById(userId);
if (admin != null) {
List<RoleInfo> roleList = userRoleService.getRoleListByUserId(Long.valueOf(userId));
return new MyUserDetails(admin, roleList);//自定义的UserDetailService
}
throw new UsernameNotFoundException("用户名或密码错误");
}
}
spring security的filter调用链:
第二张图标蓝的JwtAuthenticationTokenFilter是自定义的filter
最后一个FilterSecurityInterceptor是SpringSecurity用来进行权限认证的filter,用来检测JwtAuthenticationTokenFilter传过来的UsernamePasswordAuthenticationToken是否"authorized"、检查用户角色是否有权限(上文中的RoleInfo)及其他检测。
FilterSecurityInterceptor继承自AbstractSecurityInterceptor,role认证主要看AbstractSecurityInterceptor中的beforeInvocation方法,beforeInvocation()方法内调用了accessDecisionManager接口(AffirmativeBased继承并提供方法)的decide()方法(看源码去)
AffirmativeBased的decide方法:
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
大体意思就是用投票器的方式,多个投票器有一个投票不成功(result=-1),就返回403,access denied。