- 设计数据库
数据库权限概念
角色用户组权限关联
数据库实体图:
2、将用户权限表与springsecurity相关联
使用自定义的provider
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
ref="multipleAuthenticationProvider">
<!-- <security:user-service> <security:user name="admin" password="admin"
authorities="ROLE_USER"/> <security:user name="manager" password="manager"
authorities="ROLE_USER"/> </security:user-service> --> <!-- 内存用户测试 -->
<!-- <security:jdbc-user-service data-source-ref=""/> --> <!-- 数据库源测试 -->
<!-- <security:ldap-user-service/> --> <!-- ladp数据源测试 -->
<!-- <security:password-encoder> --> <!-- 密码encoder -->
<!-- </security:password-encoder> -->
</security:authentication-provider>
</security:authentication-manager>
<!-- 配置身份验证器 定义登陆验证过滤器 过后调用 -->
<bean id="multipleAuthenticationProvider"
class="com.bbs.security.authentication.provider.MultipleAuthenticationProvider">
<property name="authenticationProviders">
<list>
<ref bean="forendAuthenticationProvider" />
</list>
</property>
</bean>
<!-- 前台验证器并构建新用户凭证 定义登陆验证过滤器 过后调用 并调用userDetailsService 通过用户名将用户和用户的角色装配
数据库中验证用户 重新构建UsernamePasswordAuthenticationToken传递给决策管理器进行授权管理 -->
<bean id="forendAuthenticationProvider"
class="com.bbs.security.authentication.provider.ForendAuthenticationProvider">
<property name="userDetailsService" ref="forendUserDetailsService"></property>
<property name="passwordEncoder" ref="shaPasswordEncoder"></property>
<property name="saltSource" ref="saltSource"></property>
</bean>
<!-- 配置加密策略 -->
<bean id="shaPasswordEncoder"
class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<!-- 加密方式 SHA-256 -->
<constructor-arg value="256" />
</bean>
<!-- 配置密码的盐值 -->
<bean id="saltSource"
class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<!-- 以用户名作为加密盐值 -->
<property name="userPropertyToUse" value="username"></property>
</bean>
<bean id="forendUserDetailsService"
class="com.bbs.security.authentication.userdetailservice.ForendUserDetailsService"></bean>
运行顺序是解释xml,提供权限验证,manager提供管理权限验证,加载bean,配置bean。
自定义的multipleAuthenticationProvider
import java.util.List;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
public class MultipleAuthenticationProvider implements AuthenticationProvider {
private List<AuthenticationProvider> authenticationProviders;
/**
* 根据登录前的用户凭证筛选匹配的Provider,并构建新的用户凭证供登陆后的决策器调用 身份验证器
*/
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Authentication authenticationToken = null;
for (AuthenticationProvider authenticationProvider : authenticationProviders) {
if (authenticationProvider.supports(authentication.getClass())) {
authenticationToken = authenticationProvider
.authenticate(authentication);
}
}
return authenticationToken;
}
public boolean supports(Class<? extends Object> authentication) {
return true;
}
public List<AuthenticationProvider> getAuthenticationProviders() {
return authenticationProviders;
}
public void setAuthenticationProviders(
List<AuthenticationProvider> authenticationProviders) {
this.authenticationProviders = authenticationProviders;
}
}
这里才是真正的provider,前面那个只是提供赛选
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import com.bbs.security.authentication.token.ForendAuthenticationToken;
/**
* 数据库中验证用户
* @author Administrator
*
*/
public class ForendAuthenticationProvider extends DaoAuthenticationProvider
implements AuthenticationProvider {
@Override
/**
* 数据库中验证用户 重新构建UsernamePasswordAuthenticationToken传递给决策管理器进行授权管理
*/
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
ForendAuthenticationToken authenticationToken = (ForendAuthenticationToken) authentication;
//获取验证实体 userDetails
UserDetails userDetails = getUserDetailsService().loadUserByUsername(
authenticationToken.getPrincipal().toString());
// 获得的用户输入密码
String presentedPassword = authentication.getCredentials().toString();
// 获得用户名,作为加密盐值 加盐值与配置文件中的盐值属性相匹配
Object salt = getSaltSource().getSalt(userDetails);
// 判断密码是否匹配
if (!getPasswordEncoder().isPasswordValid(userDetails.getPassword(),
presentedPassword, salt)) {
throw new BadCredentialsException("password is error");
}
// 重新构建UsernamePasswordAuthenticationToken传递给决策管理器进行授权管理
return new UsernamePasswordAuthenticationToken(userDetails,
authentication.getPrincipal(), userDetails.getAuthorities());
}
/**
* 前台用户凭证 是否正确
* @param authentication
* @return
*/
@Override
public boolean supports(Class<?> authentication) {
return ForendAuthenticationToken.class.isAssignableFrom(authentication);
}
}
那它是如何从界面得到用户输入的账号与密码并且与数据库中得到的密码进行匹配,这里要进行自己定义的loginFilter进行过滤拦截
<!-- 替换默认的登陆验证Filter -->
<security:custom-filter ref="loginFilter"
before="FORM_LOGIN_FILTER" />
自己定义的loginfilter,必须继承UsernamePasswordAuthenticationFilter
<bean id="loginFilter"
class="com.bbs.security.authentication.filter.MultipleUsernamePasswordAuthenticationFilter">
<!-- 登陆页面URL -->
<property name="filterProcessesUrl" value="/login_check" />
<!-- 注入不同类型的用户凭证 -->
<property name="tokenResolvers">
<list>
<ref bean="forendAuthenticationTokenResolver" />
</list>
</property>
<!-- 校验用户名及密码,并对用户授权 -->
<property name="authenticationManager" ref="authenticationManager" />
<!-- 验证通过所执行的请求 -->
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
<!-- 验证未通过所执行的请求 -->
<property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
<!-- 自动登录 -->
<property name="rememberMeServices" ref="rememberMeServices"></property>
</bean>
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
//import com.ssh.util.CaptchaUtil;
//import com.ssh.util.ExeceptionUtil;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
//import com.ssh.util.CaptchaUtil;
/**
* 登陆验证处理类 form表单验证 并传出用户凭证
* 继承UsernamePasswordAuthenticationFilter
* @author Administrator
*
*/
public class MultipleUsernamePasswordAuthenticationFilter extends
UsernamePasswordAuthenticationFilter {
private List<AuthenticationTokenResolver> tokenResolvers;//用户配置的用户凭证标记类(用户类型)以此为依据装配具体的用户凭证 策略模式
public static final String CAPTCHA = "captcha"; // 验证码
/**
* 重写身份验证方法
*/
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) {
// 校验验证码
checkValidateCode(request);
for (AuthenticationTokenResolver tokenResolver : tokenResolvers) {
// 检验登陆类型
if (tokenResolver.support(request)) {
// 获得具体的用户凭证
Authentication authentication = tokenResolver.resolve(request);
HttpSession session = request.getSession(false);
if (session != null || getAllowSessionCreation()) {
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, authentication.getPrincipal().toString());
}
// 验证用户凭证
return this.getAuthenticationManager().authenticate(
authentication);
}
}
// throw new UnsupportedOperationException(
// ExeceptionUtil.getValue("MultipleUsernamePasswordAuthenticationFilter_attemptAuthentication"));//"No authentication token resolver found!"
return null;
}
/**
* 使用工具类校验输入的验证码是否匹配
*
* @param request
*/
protected void checkValidateCode(HttpServletRequest request) {
// if (!CaptchaUtil.validateCaptchaByRequest(request)) {
// throw new AuthenticationServiceException("ValidateCode Not Equals");
// }
}
public List<AuthenticationTokenResolver> getTokenResolvers() {
return tokenResolvers;
}
public void setTokenResolvers(
List<AuthenticationTokenResolver> tokenResolvers) {
this.tokenResolvers = tokenResolvers;
}
}
这里可以自定义全部拦截实现,可以得到用户输入的密码,后台自己实现把账号传给UsernamePasswordAuthenticationToken,接下来就是ForendUserDetailsService得到账号,实现代码为
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import javax.annotation.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Transactional;
/**
* 通过用户名将用户和用户的角色装配
* @author Administrator
*
*/
public class ForendUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
// TODO Auto-generated method stub
UserDetail userdetail=new UserDetail();
return userdetail;
}
}
UserDetail必须实现UserDetails接口
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class UserDetail implements UserDetails{
private String password;
private String username;
private Collection<GrantedAuthority> grantedauthoritys;
private boolean isEnabled; //账户是否有效
@Override
public Collection<GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return grantedauthoritys;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return "a4a88c0872bf652bb9ed803ece5fd6e82354838a9bf59ab4babb1dab322154e1";
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return "admin";
}
@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 true;
}
}
这里面可以自己从数据库中进行得到密码,赋予权限,并且自定义权限字符串,然后每次登陆时就会把权限与配置文件中进行比较
暂时发现的缺点是不能进行动态的配置权限,但是可以自己进行补充,扩展,这点等分析详细后在补充,思路是找到权限验证的类,把用户输入的URL权限与当前用户的浏览申请的URL进行比对,为了考虑运行速度的问题,还必须要把所有的浏览权限保存在内存中,否则每次都会从数据库中进行查询,减低了效率
大家都想自定义实现权限验证,这种方法告诉大家了
好了,如果有问题,请留言,由于分析难度比较大,只能慢慢来了