如果你没有用过自定义的验证的话,你的资源和资源应该有的权限都应该还是在配置文件中配置的。
类似这样:
1.
<http auto-config="true" lowercase-comparisons="false">
<intercept-url pattern="/images/**" filters="none"/>
<intercept-url pattern="/styles/**" filters="none"/>
<intercept-url pattern="/scripts/**" filters="none"/>
<intercept-url pattern="/app/admin/**" access="ROLE_ADMIN"/>
<intercept-url pattern="/app/passwordHint*" access="ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER"/>
<intercept-url pattern="/app/signup*" access="ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER"/>
<intercept-url pattern="/app/**" access="ROLE_ADMIN,ROLE_USER"/>
<form-login login-page="/login" authentication-failure-url="/login?error=true" login-processing-url="/j_security_check"/>
<remember-me user-service-ref="sysUserDao" key="e37f4b31-0c45-11dd-bd0b-0800200c9a66"/>
</http>
2.你的用户角色权限也应该像这样,是在配置文件中配置的或者是按照标准的数据库指定到一个数据源。
类似这样:
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="securityDataSource"/>
</authentication-provider>
</authentication-manager>
要做一个自定义的验证过程,就是要替换这些。
如果要实现自定义的这个过程理论上security第一步是根据你的配置文件去数据库中读取相应的资源和权限的对应关系。但是作为我们用户而言,总是从登陆开始。所以先讲如何替换用户登陆时候的过程。
在非自定义的情况下,security是这样帮我们去验证的。
当用户输入用户名密码之后,提交到一个controller去验证,这个验证的过程是:
先看你的配置文件是直接配置用户名密码在资源文件中或者是在他给你的标准表中(也就是直接指定的datasource)然后进行验证。
替换用户登陆时候的过程,也就是把这一步换成我们自己写的逻辑,不用框架去提取用户的信息判断。看实现。
首先建一个实体类,这个实体类呢必须实现UserDetails 接口:
package cn.com.xinma.frame.model;
import org.compass.annotations.Searchable;
import org.compass.annotations.SearchableComponent;
import org.compass.annotations.SearchableId;
import org.compass.annotations.SearchableProperty;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
/**
* 系统用户登录信息
*
* @author guolc
*
*/
@SuppressWarnings("serial")
@Entity
@Table(name = "sys_user")
@Searchable
@XmlRootElement
public class SysUser extends BaseObject implements Serializable, UserDetails {
private static class AuthorityComparator implements
Comparator<GrantedAuthority>, Serializable {
public int compare(GrantedAuthority g1, GrantedAuthority g2) {
// Neither should ever be null as each entry is checked before
// adding it to the set.
// If the authority is null, it is a custom authority and should
// precede others.
if (g2.getAuthority() == null) {
return -1;
}
if (g1.getAuthority() == null) {
return 1;
}
return g1.getAuthority().compareTo(g2.getAuthority());
}
}
private static SortedSet<GrantedAuthority> sortAuthorities(
Collection<GrantedAuthority> authorities) {
// Assert.notNull(authorities,
// "Cannot pass a null GrantedAuthority collection");
// Ensure array iteration order is predictable (as per
// UserDetails.getAuthorities() contract and SEC-717)
SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<GrantedAuthority>(
new AuthorityComparator());
for (GrantedAuthority grantedAuthority : authorities) {
// Assert.notNull(grantedAuthority,
// "GrantedAuthority list cannot contain any null elements");
sortedAuthorities.add(grantedAuthority);
}
return sortedAuthorities;
}
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
public void setAuthorities(Set<GrantedAuthority> authorities) {
this.authorities = authorities;
}
private boolean enabled = true;
// 实现了UserDetails之后的相关变量
private Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(0);
private String password;
private String personId;
private Set<SysGroupMember> sysGroupMembers = new HashSet<SysGroupMember>(0);
private Set<SysPmenu> sysPmenus = new HashSet<SysPmenu>(0);
private Set<SysUserRole> sysUserRoles = new HashSet<SysUserRole>(0);
private String sysuserIsValid;
private String sysuserLoginname;
private String sysuserPassword;
private String username;
public SysUser() {
}
public SysUser(String userId, String userAccount, String userPassword,
Boolean enabled, Set<SysUserRole> sysUsersRoleses,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, Collection<GrantedAuthority> authorities) {
if (((userAccount == null) || "".equals(userAccount))
|| (userPassword == null)) {
throw new IllegalArgumentException(
"Cannot pass null or empty values to constructor");
}
this.personId = userId;
this.sysuserLoginname = userAccount;
this.sysuserPassword = userPassword;
this.sysUserRoles = sysUsersRoleses;
this.username = userAccount;
this.password = userPassword;
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = Collections
.unmodifiableSet(sortAuthorities(authorities));
}
public boolean equals(Object o) {
if (!(o instanceof SysUser) || (o == null)) {
return false;
}
SysUser user = (SysUser) o;
// 具有的权限。
if (!authorities.equals(user.authorities)) {
return false;
}
// 通过Spring Security构建一个用户时,用户名和密码不能为空。
return (this.getPassword().equals(user.getPassword())
&& this.getUsername().equals(user.getUsername())
&& (this.isAccountNonExpired() == user.isAccountNonExpired())
&& (this.isAccountNonLocked() == user.isAccountNonLocked())
&& (this.isCredentialsNonExpired() == user
.isCredentialsNonExpired()) && (this.isEnabled() == user
.isEnabled()));
}
@Override
@Transient
// @OneToMany
public Collection<GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return this.authorities;
}
@Override
@Transient
public String getPassword() {
// TODO Auto-generated method stub
return this.password;
}
@Id
@SearchableId
@Column(name = "PERSON_ID", unique = true, nullable = false, length = 20)
public String getPersonId() {
return this.personId;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "sysUser")
public Set<SysGroupMember> getSysGroupMembers() {
return this.sysGroupMembers;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "sysUser")
public Set<SysPmenu> getSysPmenus() {
return this.sysPmenus;
}
@Column(name = "sysuser_is_valid", nullable = false, length = 6)
public String getSysuserIsValid() {
return this.sysuserIsValid;
}
@Column(name = "sysuser_loginname", nullable = false, length = 60)
@SearchableProperty
public String getSysuserLoginname() {
return this.sysuserLoginname;
}
@Column(name = "sysuser_password", nullable = false, length = 60)
@SearchableProperty
public String getSysuserPassword() {
return this.sysuserPassword;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "sysUser")
// @Transient
public Set<SysUserRole> getSysUserRoles() {
return this.sysUserRoles;
}
@Override
@Transient
public String getUsername() {
// TODO Auto-generated method stub
return this.username;
}
public int hashCode() {
int result = 0;
result = (sysuserLoginname != null ? sysuserLoginname.hashCode() : 0);
result = 31 * result
+ (sysuserPassword != null ? sysuserPassword.hashCode() : 0);
result = 31 * result
+ (sysuserIsValid != null ? sysuserIsValid.hashCode() : 0);
return result;
}
@Override
@Transient
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return this.accountNonExpired;
}
@Override
@Transient
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return this.accountNonLocked;
}
@Override
@Transient
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return this.credentialsNonExpired;
}
@Override
@Transient
public boolean isEnabled() {
return true;
}
public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
public void setAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = (Set<GrantedAuthority>) authorities;
}
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
public void setEnabled(boolean isEnabled) {
this.enabled = isEnabled;
}
public void setPassword(String password) {
this.password = password;
}
public void setPersonId(String personId) {
this.personId = personId;
}
public void setSysGroupMembers(Set<SysGroupMember> sysGroupMembers) {
this.sysGroupMembers = sysGroupMembers;
}
public void setSysPmenus(Set<SysPmenu> sysPmenus) {
this.sysPmenus = sysPmenus;
}
public void setSysuserIsValid(String sysuserIsValid) {
this.sysuserIsValid = sysuserIsValid;
}
public void setSysuserLoginname(String sysuserLoginname) {
this.sysuserLoginname = sysuserLoginname;
}
public void setSysuserPassword(String sysuserPassword) {
this.sysuserPassword = sysuserPassword;
}
public void setSysUserRoles(Set<SysUserRole> sysUserRoles) {
this.sysUserRoles = sysUserRoles;
}
public void setUsername(String username) {
this.username = username;
}
public String toString() {
StringBuffer sb = new StringBuffer(getClass().getSimpleName());
sb.append(" [");
sb.append("personId").append("='").append(getPersonId()).append("', ");
sb.append("sysuserLoginname").append("='")
.append(getSysuserLoginname()).append("', ");
sb.append("sysuserPassword").append("='").append(getSysuserPassword())
.append("', ");
sb.append("sysuserIsValid").append("='").append(getSysuserIsValid())
.append("', ");
sb.append("]");
return sb.toString();
}
}
这个实体类是项目里用的,比较长可能看起来比较复杂,有很多对多的关系等等,做几个说明:对新手来说很重要。
首先你实现了这个UserDetails 接口之后 只会有这么几个方法。
package cn.com.xinma.frame.model;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class tt implements UserDetails {
@Override
public Collection<GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return false;
}
}
这些方法你必须给他正确的返回值就可以了,至于你要定义哪些属性,封装哪些属性,完全可以自己去实现。举个简单的例子。
你数据库里的用户密码字段是pwd 你在上面这个实体类中定义了一个变量是private string pwd; 然后你还封装了这个属性(geter seter)
这样没问题,但是你必须保证重写的那个getPassword方法就是返回的正确的密码值。
第二步 建一个service 实现UserDetailsService接口。
实现UserDetailsService接口之后只会有一个方法:
package cn.com.xinma.frame.model;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class tt implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String arg0)
throws UsernameNotFoundException, DataAccessException {
// TODO Auto-generated method stub
return null;
}
}
通过一个用户名去获得用户的一个详细信息相信不用我实现给大家看,需要说明的是这边你需要把用户密码也查出来,返回值就是刚才我们所创建的实体类。
我给出项目中的实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package cn.com.xinma.frame.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import javax.persistence.Table;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import cn.com.xinma.frame.dao.SysUserDao;
import cn.com.xinma.frame.model.SysUser;
import cn.com.xinma.frame.model.SysUserRole;
import cn.com.xinma.frame.model.User;
/**
* 该类的主要作用是为Spring Security提供一个经过用户认证后的UserDetails。
* 该UserDetails包括用户名、密码、是否可用、是否过期等信息。
*
*/
@Service("UserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private SysUserDao sysUsersDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,
DataAccessException {
Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
// 得到用户的权限
auths = sysUsersDao.loadUserAuthoritiesByName(username);
System.out.println(username+"的权限:");
JSONArray jsObject = JSONArray.fromObject(auths);
System.out.println(jsObject);
// 根据用户名取得一个SysUsers对象,以获取该用户的其他信息。
SysUser user = sysUsersDao.findByUserAccount(username);
Set<SysUserRole> ss= user.getSysUserRoles();
SysUser sysUser = new SysUser(user.getPersonId(), user.getSysuserLoginname(), user.getSysuserPassword(), true, user.getSysUserRoles(), true, true, true, auths);
return sysUser;
}
// set PubUsersDao
public void setSysUsersDao(SysUserDao sysUsersDao) {
this.sysUsersDao = sysUsersDao;
}
public SysUserDao getSysUsersDao() {
return sysUsersDao;
}
}
代码写完也总得配置才会起效
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="UserDetailsService">
<!-- 用户密码加密 just like 899312{gaobing} to md5 -->
<password-encoder ref="MyPasswordEncode">
<salt-source user-property="username" />
</password-encoder>
</authentication-provider>
</authentication-manager>
红色背景指向的是什么大家想想应该就明白了。
这样完成之后用户提交登陆页面的时候就会进入到这个方法,按照你的逻辑去查询用户的信息,最后返回一个实现了userdetail的对象。
如果你看过之前讲密码加密(盐值加密)的话 应该会继续走到你自己定义的密码编辑器那边。
如果你的登陆未成功 , 或者没有进入到你自定义的密码编辑器中,可能是应为在你的实体类中 ,重写的那些方法没有给正确的值。比如isEnabled() 这个方法如果返回的是false,不可用,当然不会继续去判断密码之类
好了就这么多 ,讲了蛮多不正确的欢迎纠正。