本篇主要讲自定义用户认证与授权
在安全管理二中我们实现了自定义数据表实现了用户权限管理,但我们发现,认证与授权的sql写死在了配置文件中,而且字段有限,现实开发中,我们的用户信息需要更多的字段,很明显,这种写死在配置文件中的认证与授权严重不满足我们的需求,我们需要通过自定义用户认证与授权来完成更灵活,更有效的用户权限管理。在spring security中我们需要通过扩展userDetails,以及编写自己的userDetailsService来实现自定义用户认证与授权
第一步:扩展userDetails
package com.longzhun.fpm.security;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.userdetails.UserDetails;
public class BaseUserDetails implements UserDetails {
//账号
protected String username;
//密码
protected String password;
//是否可用
protected boolean enabled;
//用户权限集合
protected GrantedAuthority[] authorties;
//账号是否过期
protected boolean accountNonExpired;
//账号是否被锁
protected boolean accountNonLocked;
//密码是否过期
protected boolean credentialsNonExpired;
public BaseUserDetails(String username, String password, boolean enabled,
GrantedAuthority[] authorties) {
this(username,password,enabled,authorties,true,true,true);
}
public BaseUserDetails(String username, String password, boolean enabled,
GrantedAuthority[] authorties, boolean accountNonExpired,
boolean accountNonLocked, boolean credentialsNonExpired) {
if(StringUtils.isBlank(username) || StringUtils.isBlank(password)){
throw new IllegalArgumentException("不允许用户帐号或者密码为空!");
}
this.username = username;
this.password = password;
this.enabled = enabled;
this.authorties = authorties;
this.accountNonExpired = accountNonExpired;
this.accountNonLocked = accountNonLocked;
this.credentialsNonExpired = credentialsNonExpired;
}
public GrantedAuthority[] getAuthorities() {
return this.authorties;
}
public String getPassword() {
return this.password;
}
public String getUsername() {
return this.username;
}
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
public boolean isEnabled() {
return this.enabled;
}
}
第二步:新建用户信息扩展类UserInfo
目前咱们就用了一个扩展字段Id
package com.longzhun.fpm.security;
import org.springframework.security.GrantedAuthority;
public class UserInfo extends BaseUserDetails {
private String id;
public UserInfo(String username, String password, boolean enabled,
GrantedAuthority[] authorties) {
super(username, password, enabled, authorties);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
第三步:为了更好的与数据库建立映射关系,实现dao与service 方法
我新建了一个bean
package com.longzhun.fpm.security;
import java.util.HashSet;
import java.util.Set;
import com.longzhun.fpm.dto.Authority;
public class Security {
private String id;
private String username;
private String password;
private boolean enabled;
private Set<Authority> authories = new HashSet<Authority>(0);
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<Authority> getAuthories() {
return authories;
}
public void setAuthories(Set<Authority> authories) {
this.authories = authories;
}
}
第四步:编写自己的userDetailsService实现类
package com.longzhun.fpm.security.service.impl;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.longzhun.fpm.dto.Authority;
import com.longzhun.fpm.security.Security;
import com.longzhun.fpm.security.UserInfo;
import com.longzhun.fpm.security.dao.SecurityDao;
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
@Qualifier("securityDao")
private SecurityDao securityDao;
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
Security security = securityDao.getUserInfoByUsername(username);
if(security == null){
throw new UsernameNotFoundException("用户 : "+username+"不存在!");
}
UserInfo userInfo = new UserInfo(username, security.getPassword(),security.isEnabled(), getGrantedAuthorities(security));
userInfo.setId(security.getId());
return userInfo;
}
private GrantedAuthority [] getGrantedAuthorities(Security security){
Set<GrantedAuthority> auths = new HashSet<GrantedAuthority>();
for(Authority authority:security.getAuthories()){
auths.add(new GrantedAuthorityImpl(authority.getValue()));
}
return auths.toArray(new GrantedAuthority[auths.size()]);
}
}
第五步:修改applicationContext-security.xml
将安全管理二写的:
<ss:authentication-provider>
<ss:password-encoder hash="md5">
<ss:salt-source user-property="username"/> 盐值 密码+用户名=密码
</ss:password-encoder>
<ss:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username,password,enabled from test_user where username=?"
authorities-by-username-query="select u.username,r.role_name authority from test_user u
join test_user_role ur on u.id = ur.user_id
join test_role r on r.id = ur.role_id
where u.username =?"/>
</ss:authentication-provider>
替换成:
<ss:authentication-provider user-service-ref="userDetailsService"></ss:authentication-provider>