Spring Security(以下简称SS)中默认存在一个org.springframework.security.core.userdetails.UserDetails类,该类是SS内置的,提供了几个简单的属性,如userName,password,enabled等等,但这些属性不能完全适应我们现在的系统,所有一般需要自定义自己的UserDetails。自定义UserDetails需要继承SS内置的UserDetails,之所以继承内置的UserDetails,是因为在SS中可以通过SecurityContextHolder取得用户凭证和用户所有的信息,用户信息都存放在系统内置的UserDetails中,如果继承之后,即可进行向下转型,变成我们自己的定义的UserDetails了。
参考代码如下,以下自定义的UserDetails中加入了用户拥有的设备,用户角色,用户权限等等,假设我需要在用户登录之后就加载这些用户关联的信息,那么加入UserDetails之后,在任何位置就能通过SecurityContextHolder取到了。
1 /** 2 * 3 * @author bigbang 通过扩展UserDetails接口扩展自己的用户信息 4 */ 5 public class MyUserDetails implements UserDetails { 6 7 private static final long serialVersionUID = 1L; 8 private User user; 9 private List<Privilege> privileges; 10 private Collection<GrantedAuthority> roles; 11 private List<Device> devices; 12 13 public MyUserDetails() { 14 } 15 16 public MyUserDetails(User user, List<Privilege> privileges, Collection<GrantedAuthority> roles, List<Device> devices) { 17 super(); 18 this.user = user; 19 this.privileges = privileges; 20 this.roles = roles; 21 this.devices = devices; 22 } 23 24 public List<Privilege> getPrivileges() { 25 return privileges; 26 } 27 28 public List<Device> getDevices() { 29 return devices; 30 } 31 32 @Override 33 public Collection<? extends GrantedAuthority> getAuthorities() { 34 return this.roles; 35 } 36 37 @Override 38 public String getPassword() { 39 return user.getPassWord(); 40 } 41 42 @Override 43 public String getUsername() { 44 return user.getUserName(); 45 } 46 47 @Override 48 public boolean isAccountNonExpired() { 49 return true; 50 } 51 52 @Override 53 public boolean isAccountNonLocked() { 54 return true; 55 } 56 57 @Override 58 public boolean isCredentialsNonExpired() { 59 return true; 60 } 61 62 @Override 63 public boolean isEnabled() { 64 return true; 65 } 66 67 public User getUser() { 68 return user; 69 } 70 71 public void setUser(User user) { 72 this.user = user; 73 } 74 75 public Collection<GrantedAuthority> getRoles() { 76 return roles; 77 } 78 79 public void setRoles(Collection<GrantedAuthority> roles) { 80 this.roles = roles; 81 } 82 83 public void setPrivileges(List<Privilege> privileges) { 84 this.privileges = privileges; 85 } 86 87 public void setDevices(List<Device> devices) { 88 this.devices = devices; 89 } 90 91 }
在定义之后,接着就是怎么使用了。因为使用了自己的凭证信息,所以必须在ss的配置文件中加入自定义的认证管理器(AuthenticationManager)与认证数据容器(AuthenticationProvider).
第一步是在http标签添加认证管理器:
1 <http auto-config="true" use-expressions="true" authentication-manager-ref="myAuthenticationManager"> 2 </http>
第二步在认证管理器中添加认证容器:
1 <authentication-manager id="myAuthenticationManager"> 2 <authentication-provider user-service-ref="accountSecurityService"> 3 <password-encoder hash="bcrypt" base64="true"/> 4 </authentication-provider> 5 </authentication-manager>
它其中指定了一个用户信息服务,那么对应关系即可以理解为: 用户信息服务——>认证数据提供者——>认证管理器,通过一层层包装用户数据。
自定义用户信息服务才是我们真正使用自定义UserDetails的核心,它实现了UserDetailsService接口,它所需要实现的方法只有一个 loadUserByUserName(String userName),返回UserDetails对象。就是根据用户名查询出一个UserDetails。此时我们可以返回一个自定义的UserDetails。
1 public class AccountSecurityService implements UserDetailsService { 2 3 @Autowired 4 private IUserService userService; 5 6 @Override 7 public UserDetails loadUserByUsername(String userName) 8 throws UsernameNotFoundException { 9 MyUserDetails details = null; 10 com.bigbang.entity.User user = userService.getUserDetail(userName); //查询一次用户所有关联信息 11 if (user == null) 12 return null; 13 details = new MyUserDetails(user,null,null,null); 14 return details; 15 } 16 17 }
此处仅仅是查询出了用户的基本信息,与用户相关联的角色与设备并没有设置进去,这样做的好处是防止用户如果登录多次就会多次查询,如果登录失败同样也会查询,造成不必要的查询。那么这部分信息,我们可以放到登录成功之后查询,这部分属于认证阶段,暂时不说。