在数字化转型中,选择合适的跨平台开发框架不仅能提高效率,还有助于确保数据安全与合规性。
715
2023-03-15
详解spring security四种实现方式
spring security实现方式大致可以分为这几种:
1.配置文件实现,只需要在配置文件中指定拦截的url所需要权限、配置userDetailsService指定用户名、密码、对应权限,就可以实现。
2.实现UserDetailsService,loadUserByUsername(String userName)方法,根据userName来实现自己的业务逻辑返回UserDetails的实现类,需要自定义User类实现UserDetails,比较重要的方法是getAuthorities(),用来返回该用户所拥有的权限。
3.通过自定义filter重写spring security-,实现动态过滤用户权限。
4.通过自定义filter重写spring security-,实现自定义参数来检验用户,并且过滤权限。
1.最简单配置spring-security.xml,实现1
xmlns:security="http://springframework.org/schema/security" xmlns:p="http://springframework.org/schema/p" xmlns:xsi="http://w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans-4.0.xsd http://springframework.org/schema/security http://springframework.org/schema/security/spring-security-4.0.xsd"> authorities="ROLE_USER" />
xmlns:security="http://springframework.org/schema/security"
xmlns:p="http://springframework.org/schema/p" xmlns:xsi="http://w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://springframework.org/schema/beans
http://springframework.org/schema/beans/spring-beans-4.0.xsd
http://springframework.org/schema/security
http://springframework.org/schema/security/spring-security-4.0.xsd">
authorities="ROLE_USER" />
authorities="ROLE_USER" />
2.实现UserDetailsService
先整理下spring secruity验证流程:
springSecurity的登录验证是由org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter这个过滤器来完成的,在该类的父类AbstractAuthenticationProcessingFilter中有一个AuthenticationManager接口属性,验证工作主要是通过这个AuthenticationManager接口的实例来完成的。在默认情况下,springSecurity框架会把org.springframework.security.authentication.ProviderManager类的实例注入到该属性
UsernamePasswordAuthenticationFilter的验证过程如下:
1. 首先过滤器会调用自身的attemptAuthentication方法,从request中取出authentication, authentication是在org.springframework.security.web.context.SecurityContextPersistenceFilter过滤器中通过捕获用户提交的登录表单中的内容生成的一个org.springframework.security.core.Authentication接口实例.
2. 拿到authentication对象后,过滤器会调用ProviderManager类的authenticate方法,并传入该对象
3.ProviderManager类的authenticate方法中会调用类中的List
4.provider的实现类在验证用户时,会调用userDetailsService的实现类的loadUserByUsername方法来获取用户信息,
首先spring-security配置文件
xmlns:beans="http://springframework.org/schema/beans" xmlns:xsi="http://w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans-4.3.xsd http://springframework.org/schema/security http://springframework.org/schema/security/spring-security.xsd">
xmlns:beans="http://springframework.org/schema/beans"
xmlns:xsi="http://w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://springframework.org/schema/beans
http://springframework.org/schema/beans/spring-beans-4.3.xsd
http://springframework.org/schema/security
http://springframework.org/schema/security/spring-security.xsd">
userDetailsService实现:
/**
*
*/
package com.ultrapower.me.util.security.support;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.ultrapower.me.util.Constants;
import com.ultrapower.me.util.dbDao.SpringBeanUtil;
import com.ultrapower.me.util.security.SecurityManager;
import com.ultrapower.me.util.security.entity.Resource;
import com.ultrapower.me.util.security.entity.Role;
import com.ultrapower.me.util.security.entity.User;
import com.ultrapower.me.util.task.PasswordUtils;
public class SecurityManagerSupport implements UserDetailsService{
private Log log = LogFactory.getLog(this.getClass().getName());
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
// List
log.info("SecurityManagerSupport.loadUserByUsername.userName:"+userName);
User user =null;
if("admin".equals(userName)){
Set
Role role = new Role();
role.setRoleid("ROLE_USER");
role.setRoleName("ROLE_USER");
Set
Resource res = new Resource();
res.setResid("ME001");
res.setResName("首页");
res.setResUrl("/jsp/index/main.jsp");
res.setType("ROLE_USER");
res.setRoles(roles);
resources.add(res);
role.setResources(resources);
roles.add(role);
user = new User();
user.setAccount("admin");
user.setDisabled(false);
user.setPassword(PasswordUtils.entryptPassword(Constants.securityKey));
log.info(user.getPassword());
user.setRoles(roles);
}
return user;//返回UserDetails的实现user不为空,则验证通过
}
}
UserDetails实现:
/**
*
*/
package com.ultrapower.me.util.security.entity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements UserDetails {
private static final long serialVersionUID = 8026813053768023527L;
private String account;
private String name;
private String password;
private boolean disabled;
private Set
private Map
/**
* The default constructor
*/
public User() {
}
/**
* Returns the authorites string
*
* eg.
* downpour --- ROLE_ADMIN,ROLE_USER
* robbin --- ROLE_ADMIN
*
* @return
*/
public String getAuthoritiesString() {
List
for(GrantedAuthority authority : this.getAuthorities()) {
authorities.add(authority.getAuthority());
}
return StringUtils.join(authorities, ",");
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
// 根据自定义逻辑来返回用户权限,如果用户权限返回空或者和拦截路径对应权限不同,验证不通过
if(!roles.isEmpty()){
List
GrantedAuthority au = new SimpleGrantedAuthority("ROLE_USER");
list.add(au);
return list;
}
return null;
}
/*
* 密码
*/
public String getPassword() {
return password;
}
/*
* 用户名
*/
public String getUsername() {
return name;
}
/*
*帐号是否不过期,false则验证不通过
*/
public boolean isAccountNonExpired() {
return true;
}
/*
* 帐号是否不锁定,false则验证不通过
*/
public boolean isAccountNonLocked() {
return true;
}
/*
* 凭证是否不过期,false则验证不通过
*/
public boolean isCredentialsNonExpired() {
return true;
}
/*
* 该帐号是否启用,false则验证不通过
*/
public boolean isEnabled() {
return !disabled;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @return the disabled
*/
public boolean isDisabled() {
return disabled;
}
/**
* @return the roles
*/
public Set
return roles;
}
/**
* @return the roleResources
*/
public Map
// init roleResources for the first time
System.out.println("---------------------------------------------------");
if(this.roleResources == null) {
this.roleResources = new HashMap
for(Role role : this.roles) {
String roleName = role.getRoleName();
Set
for(Resource resource : resources) {
String key = roleName + "_" + resource.getType();
if(!this.roleResources.containsKey(key)) {
this.roleResources.put(key, new ArrayList
}
this.roleResources.get(key).add(resource);
}
}
}
return this.roleResources;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @param disabled the disabled to set
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
/**
* @param roles the roles to set
*/
public void setRoles(Set
this.roles = roles;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public void setRoleResources(Map
this.roleResources = roleResources;
}
}
3.实现动态过滤用户权限
在spring-security配置文件的http标签中添加如下配置
在spring-security配置文件中添加如下配置
securityInterceptor继承AbstractSecurityInterceptor过滤器,实现Filter过滤器
package com.ultrapower.me.util.security.interceptor;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
public class SecurityInterceptor extends AbstractSecurityInterceptor implements Filter{
//配置文件注入
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub\
FilterInvocation fi = new FilterInvocation(request, response, chain);
//fi里面有一个被拦截的url
//里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//执行下一个-
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public Class> getSecureObjectClass() {
// TODO Auto-generated method stub
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
// TODO Auto-generated method stub
return this.securityMetadataSource;
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
登陆后,每次访问资源都会被这个-拦截,会执行doFilter这个方法,这个方法调用了invoke方法,其中fi断点显示是一个url(可能重写了toString方法吧,但是里面还有一些方法的),最重要的是beforeInvocation这个方法,它首先会调用MyInvocationSecurityMetadataSource类的getAttributes方法获取被拦截url所需的权限,在调用MyAccessDecisionManager类decide方法判断用户是否够权限。弄完这一切就会执行下一个-。
secureResourceFilterInvocationDefinitionSource实现
/**
*
*/
package com.ultrapower.me.util.security.interceptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
private PathMatcher matcher;
private static Map
/*
* 初始化用户权限,为了简便操作没有从数据库获取
* 实际操作可以从数据库中获取所有资源路径url所对应的权限
*/
public void afterPropertiesSet() throws Exception {
this.matcher = new AntPathMatcher();//用来匹配访问资源路径
Collection
ConfigAttribute ca = new SecurityConfig("ROLE_USER");
atts.add(ca);
map.put("/jsp/index/main.jsp", atts);
Collection ConfigAttribute cano = new SecurityConfig("ROLE_NO"); attsno.add(cano); map.put("/http://blog.csdn-/u012367513/article/details/other.jsp", attsno); } @Override public Collection throws IllegalArgumentException { // TODO Auto-generated method stub FilterInvocation filterInvocation = (FilterInvocation) object; String requestURI = filterInvocation.getRequestUrl(); //循环资源路径,当访问的Url和资源路径url匹配时,返回该Url所需要的权限 for(Iterator Map.Entry String url = entry.getKey(); if(matcher.match(url, requestURI)) { return map.get(requestURI); } } return null; } @Override public Collection // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions() */ @SuppressWarnings("rawtypes") public Collection getConfigAttributeDefinitions() { return null; } /* (non-Javadoc) * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class) */ public boolean supports(@SuppressWarnings("rawtypes") Class clazz) { return true; } /** * * @param filterInvocation * @return */ @SuppressWarnings("unchecked") private Map ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext(); return (Map } } mesecurityAccessDecisionManager实现 package com.ultrapower.me.util.security.interceptor; import java.util.Collection; import java.util.Iterator; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; public class SecurityAccessDecisionManager implements AccessDecisionManager { /** * 检查用户是否够权限访问资源 * authentication 是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息 * object 是url * configAttributes 所需的权限 * @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection) */ @Override public void decide(Authentication authentication, Object object, Collection throws AccessDeniedException, InsufficientAuthenticationException { // 对应url没有权限时,直接跳出方法 if(configAttributes == null){ return; } Iterator //判断用户所拥有的权限,是否符合对应的Url权限,如果实现了UserDetailsService,则用户权限是loadUserByUsername返回用户所对应的权限 while(ite.hasNext()){ ConfigAttribute ca=ite.next(); String needRole=((SecurityConfig)ca).getAttribute(); for(GrantedAuthority ga : authentication.getAuthorities()){ System.out.println(":::::::::::::"+ga.getAuthority()); if(needRole.equals(ga.getAuthority())){ return; } } } //注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面 throw new AccessDeniedException("no right"); } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class> clazz) { return true; } } 4.实现AuthenticationProvider,自定义参数验证 这种验证以前项目用过,现在没有写示例代码,先写下大概流程和需要用到的类 这种验证的好处:可以在自定义登录界面添加登录时需要的参数,如多个验证码等、可以修改默认登录名称和密码的参数名 整体流程: 1.用户登录时,先经过自定义的passcard_filter过滤器,该过滤器继承了AbstractAuthenticationProcessingFilter,并且绑定了登录失败和成功时需要的处理器(跳转页面使用) 2.执行attemptAuthentication方法,可以通过request获取登录页面传递的参数,实现自己的逻辑,并且把对应参数set到AbstractAuthenticationToken的实现类中 3.验证逻辑走完后,调用 this.getAuthenticationManager().authenticate(token);方法,执行AuthenticationProvider的实现类的supports方法 4.如果返回true则继续执行authenticate方法 5.在authenticate方法中,首先可以根据用户名获取到用户信息,再者可以拿自定义参数和用户信息做逻辑验证,如密码的验证 6.自定义验证通过以后,获取用户权限set到User中,用于springSecurity做权限验证 7.this.getAuthenticationManager().authenticate(token)方法执行完后,会返回Authentication,如果不为空,则说明验证通过 8.验证通过后,可实现自定义逻辑操作,如记录cookie信息 9.attemptAuthentication方法执行完成后,由springSecuriy来进行对应权限验证,成功于否会跳转到相对应处理器设置的界面。 1.自定义PassCardAuthenticationToken类,继承AbstractAuthenticationToken类,用于定义参数,需要实现的方法 /** * 凭证,用户密码 */ @Override public Object getCredentials() { return password; } /** * 当事人,登录名 用户Id */ @Override public Object getPrincipal() { return userID; } 2.User类要实现Authentication,需要实现的方法 /** * 返回用户所属权限 */ @Override public Collection return this.accesses; } @Override public Object getCredentials() { return null; } @Override public Object getDetails() { return null; } /** * 登录名称 */ @Override public Object getPrincipal() { return loginName; } /** * 是否认证 */ @Override public boolean isAuthenticated() { return this.authenticated; } /** * 设置是否认证字段 */ @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { this.authenticated=isAuthenticated; } 3.需要userService实现AuthenticationProvider的 authenticate(Authentication authentication)方法 @SuppressWarnings("unchecked") @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { PassCardAuthenticationToken token=(PassCardAuthenticationToken)authentication; /* * 这里进行逻辑认证操作,可以获取token中的属性来自定义验证逻辑,代码验证逻辑可以不用管 * 如果使用UserDetailsService的实现类来验证,就只能获取userName,不够灵活 */ if(token.getUserID()!=null&&token.getPassword()!=null){ User user=(User)this.getDao().executeQueryUnique("User.loadByLoginName", QueryCmdType.QUERY_NAME, token.getUserID()); String password=token.getPassword(); if(this.passwordEncoder!=null){ password=this.passwordEncoder.encodePassword(password, null); } if(!password.equalsIgnoreCase(user.getPassword())){ token.setErrCode("2"); return null; } if( token.isEnablePasscard() && usePassCard ){//token中激活密码卡且系统使用密码卡 int position1=((token.getRow1()-1)*7)+token.getColumn1(); int position2=((token.getRow2()-1)*7)+token.getColumn2(); //System.out.println( "---pos:"+position1+"---"+position2 ); if(user.getPassCardId()==null){ token.setErrCode("10"); return null; } PassCard passcard=this.passCardDao.findById(user.getPassCardId(), false); if(passcard==null||passcard.getStatus()==PassCardHelper.STATUS_CANCEL ){ token.setErrCode("10"); return null; } if(passcard.getConfusedContent()==null || passcard.getConfusedContent().length()<7*7*32 ){ token.setErrCode("10"); return null; } String content=passcard.getConfusedContent(); int perLen=content.length()/49; String str1=content.substring((position1-1)*perLen, position1*perLen); String str2=content.substring((position2-1)*perLen, position2*perLen); String inputStr1=token.getCard1(); String inputStr2=token.getCard2(); if(this.passwordEncoder!=null){ inputStr1 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr1)); inputStr2 = md5.getMD5ofStr(md5.gethttp://MD5ofStr(inputStr2)); } if((!str1.equalsIgnoreCase(inputStr1))||(!str2.equalsIgnoreCase(inputStr2))){ token.setErrCode("10"); return null; } } user.setLastIp(token.getIp()); user.setLastLogin(new Date()); this.getDao().saveOrUpdate(user); user.setAuthenticated(true); /* * 导入一次角色权限,并且把权限set到User中,用于spring验证用户权限(getAuthorities方法) */ List Set for(UserRole ur:userRoles){ accesses.add(ur.getRole()); } user.getOrg().getOrgName(); if(user.getOrg().getCertTypes()!=null) user.getOrg().getCertTypes().size();//延迟载入一下 user.setAccesses(accesses); return user; } return null; } 重写supports(Class extends Object> authentication)方法,authentication要 /** * 如果此处验证不通过,是不会执行authentication方法的 */ @Override public boolean supports(Class extends Object> authentication) { return authentication.equals(PassCardAuthenticationToken.class); } 4.定义filter,实现AbstractAuthenticationProcessingFilter的attemptAuthentication方法,用于获取在登录页面传递过来的参数,spring默认只获取userName(j_username),password(j_username),而且实现UserDetailsService时只传递username import java.io.IOException; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.util.StringUtils; import cn.edu.jszg.cert.user.UserLog; import cn.edu.jszg.cert.user.UserLogService; import cn.edu.jszg.cert.web.WebApplicationConfiguration; import cn.edu.jszg.cert.web.controller.portal.auth.RemoteDataValidator; import com.google.code.kaptcha.servlet.KaptchaServlet; public class PasscardAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter { private String successPage = "/home/admin/index"; private String failurePage = "/public/adminLoginEntry"; private boolean forward = false; private boolean useVerifyCode=true; private String certLoginUrl; static Logger logger = Logger.getLogger(PasscardAuthenticationProcessingFilter.class); private WebApplicationConfiguration config; private UserLogService userLogService; public void setConfig(WebApplicationConfiguration config) { this.config = config; } /** * 实现AbstractAuthenticationProcessingFilter的有参构造 * 没记错的话,相当于该filter的访问路径 */ protected PasscardAuthenticationProcessingFilter() { super("/adminLoginCheck"); } public void setUseVerifyCode(boolean useVerifyCode) { this.useVerifyCode = useVerifyCode; } public void setUserLogService(UserLogService userLogService) { this.userLogService = userLogService; } public boolean validate(HttpServletRequest request) { String userId = request.getParameter("username"); String md2 = request.getParameter("m"); String l = request.getParameter("l"); if (userId == null || md2 == null || l == null) { return false; } long longTime = Long.parseLong(l); if (longTime < new Date().getTime()) { return false; } try { String md1 = RemoteDataValidator.genExamMd5Digest(userId, longTime); if (md1.equals(md2)) return true; } catch (Exception e) { //e.printStackTrace(); } return false; } /** * 可以通过request获取页面传递过来的参数,并且set到相应的token中 */ @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { // logger.warn("-----------------start证书登录用户----------"); HttpSession s = request.getSession(true); PassCardAuthenticationToken token = new PassCardAuthenticationToken(); String verifyCode = request.getParameter("verifyCode"); String userID = request.getParameter("username"); //....此处省略获取参数,并且验证、赋值的逻辑 Authentication auth = null; try { //此处调用getAuthenticationManager的authenticate方法,当supports方法返回true时执行authenticate方法 auth = this.getAuthenticationManager().authenticate(token); //此处为登录成功后,相应的处理逻辑 if (auth == null || !auth.isAuthenticated()) { s.setAttribute("__login_error", token.getErrCode()); } else { s.removeAttribute("__login_error"); s.removeAttribute("__login_username"); s.removeAttribute("__cert_userid"); if( token.isEnablePasscard()) { s.removeAttribute("__passcard_row1"); s.removeAttribute("__passcard_row2"); s.removeAttribute("__passcard_column1"); s.removeAttribute("__passcard_column2"); } } } catch (AuthenticationException e) { s.setAttribute("__login_error", token.getErrCode()); throw e; } return auth; } public void setSuccessPage(String successPage) { this.successPage = successPage; } public void setFailurePage(String failurePage) { this.failurePage = failurePage; } public void setForward(boolean forward) { this.forward = forward; } public void setCertLoginUrl(String certLoginUrl) { this.certLoginUrl = certLoginUrl; } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); /* *该处理器实现了AuthenticationSuccessHandler, AuthenticationFailureHandler *用于处理登录成功或者失败后,跳转的界面 */ AuthenticationResultHandler handler = new AuthenticationResultHandler(); handler.setForward(forward); handler.setLoginFailurePage(failurePage); handler.setLoginSuccessPage(successPage); handler.setCertLoginUrl(certLoginUrl); //设置父类中的处理器 this.setAuthenticationSuccessHandler(handler); this.setAuthenticationFailureHandler(handler); } } 最后为spring-security配置文件中的配置,需要添加authentication-provider的引用,和filter的配置 还要在http中添加
ConfigAttribute cano = new SecurityConfig("ROLE_NO");
attsno.add(cano);
map.put("/http://blog.csdn-/u012367513/article/details/other.jsp", attsno);
}
@Override
public Collection
throws IllegalArgumentException {
// TODO Auto-generated method stub
FilterInvocation filterInvocation = (FilterInvocation) object;
String requestURI = filterInvocation.getRequestUrl();
//循环资源路径,当访问的Url和资源路径url匹配时,返回该Url所需要的权限
for(Iterator
Map.Entry
String url = entry.getKey();
if(matcher.match(url, requestURI)) {
return map.get(requestURI);
}
}
return null;
}
@Override
public Collection
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()
*/
@SuppressWarnings("rawtypes")
public Collection getConfigAttributeDefinitions() {
return null;
}
/* (non-Javadoc)
* @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class)
*/
public boolean supports(@SuppressWarnings("rawtypes") Class clazz) {
return true;
}
/**
*
* @param filterInvocation
* @return
*/
@SuppressWarnings("unchecked")
private Map
ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
return (Map
}
}
mesecurityAccessDecisionManager实现
package com.ultrapower.me.util.security.interceptor;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
public class SecurityAccessDecisionManager implements AccessDecisionManager {
/**
* 检查用户是否够权限访问资源
* authentication 是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息
* object 是url
* configAttributes 所需的权限
* @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
*/
@Override
public void decide(Authentication authentication, Object object,
Collection
throws AccessDeniedException, InsufficientAuthenticationException {
// 对应url没有权限时,直接跳出方法
if(configAttributes == null){
return;
}
Iterator
//判断用户所拥有的权限,是否符合对应的Url权限,如果实现了UserDetailsService,则用户权限是loadUserByUsername返回用户所对应的权限
while(ite.hasNext()){
ConfigAttribute ca=ite.next();
String needRole=((SecurityConfig)ca).getAttribute();
for(GrantedAuthority ga : authentication.getAuthorities()){
System.out.println(":::::::::::::"+ga.getAuthority());
if(needRole.equals(ga.getAuthority())){
return;
}
}
}
//注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面
throw new AccessDeniedException("no right");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class> clazz) {
return true;
}
}
4.实现AuthenticationProvider,自定义参数验证
这种验证以前项目用过,现在没有写示例代码,先写下大概流程和需要用到的类
这种验证的好处:可以在自定义登录界面添加登录时需要的参数,如多个验证码等、可以修改默认登录名称和密码的参数名
整体流程:
1.用户登录时,先经过自定义的passcard_filter过滤器,该过滤器继承了AbstractAuthenticationProcessingFilter,并且绑定了登录失败和成功时需要的处理器(跳转页面使用)
2.执行attemptAuthentication方法,可以通过request获取登录页面传递的参数,实现自己的逻辑,并且把对应参数set到AbstractAuthenticationToken的实现类中
3.验证逻辑走完后,调用 this.getAuthenticationManager().authenticate(token);方法,执行AuthenticationProvider的实现类的supports方法
4.如果返回true则继续执行authenticate方法
5.在authenticate方法中,首先可以根据用户名获取到用户信息,再者可以拿自定义参数和用户信息做逻辑验证,如密码的验证
6.自定义验证通过以后,获取用户权限set到User中,用于springSecurity做权限验证
7.this.getAuthenticationManager().authenticate(token)方法执行完后,会返回Authentication,如果不为空,则说明验证通过
8.验证通过后,可实现自定义逻辑操作,如记录cookie信息
9.attemptAuthentication方法执行完成后,由springSecuriy来进行对应权限验证,成功于否会跳转到相对应处理器设置的界面。
1.自定义PassCardAuthenticationToken类,继承AbstractAuthenticationToken类,用于定义参数,需要实现的方法
/**
* 凭证,用户密码
*/
@Override
public Object getCredentials() {
return password;
}
/**
* 当事人,登录名 用户Id
*/
@Override
public Object getPrincipal() {
return userID;
}
2.User类要实现Authentication,需要实现的方法
/**
* 返回用户所属权限
*/
@Override
public Collection
return this.accesses;
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getDetails() {
return null;
}
/**
* 登录名称
*/
@Override
public Object getPrincipal() {
return loginName;
}
/**
* 是否认证
*/
@Override
public boolean isAuthenticated() {
return this.authenticated;
}
/**
* 设置是否认证字段
*/
@Override
public void setAuthenticated(boolean isAuthenticated)
throws IllegalArgumentException {
this.authenticated=isAuthenticated;
}
3.需要userService实现AuthenticationProvider的 authenticate(Authentication authentication)方法
@SuppressWarnings("unchecked")
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
PassCardAuthenticationToken token=(PassCardAuthenticationToken)authentication;
/*
* 这里进行逻辑认证操作,可以获取token中的属性来自定义验证逻辑,代码验证逻辑可以不用管
* 如果使用UserDetailsService的实现类来验证,就只能获取userName,不够灵活
*/
if(token.getUserID()!=null&&token.getPassword()!=null){
User user=(User)this.getDao().executeQueryUnique("User.loadByLoginName", QueryCmdType.QUERY_NAME, token.getUserID());
String password=token.getPassword();
if(this.passwordEncoder!=null){
password=this.passwordEncoder.encodePassword(password, null);
}
if(!password.equalsIgnoreCase(user.getPassword())){
token.setErrCode("2");
return null;
}
if( token.isEnablePasscard() && usePassCard ){//token中激活密码卡且系统使用密码卡
int position1=((token.getRow1()-1)*7)+token.getColumn1();
int position2=((token.getRow2()-1)*7)+token.getColumn2();
//System.out.println( "---pos:"+position1+"---"+position2 );
if(user.getPassCardId()==null){
token.setErrCode("10");
return null;
}
PassCard passcard=this.passCardDao.findById(user.getPassCardId(), false);
if(passcard==null||passcard.getStatus()==PassCardHelper.STATUS_CANCEL ){
token.setErrCode("10");
return null;
}
if(passcard.getConfusedContent()==null || passcard.getConfusedContent().length()<7*7*32 ){
token.setErrCode("10");
return null;
}
String content=passcard.getConfusedContent();
int perLen=content.length()/49;
String str1=content.substring((position1-1)*perLen, position1*perLen);
String str2=content.substring((position2-1)*perLen, position2*perLen);
String inputStr1=token.getCard1();
String inputStr2=token.getCard2();
if(this.passwordEncoder!=null){
inputStr1 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr1));
inputStr2 = md5.getMD5ofStr(md5.gethttp://MD5ofStr(inputStr2));
}
if((!str1.equalsIgnoreCase(inputStr1))||(!str2.equalsIgnoreCase(inputStr2))){
token.setErrCode("10");
return null;
}
}
user.setLastIp(token.getIp());
user.setLastLogin(new Date());
this.getDao().saveOrUpdate(user);
user.setAuthenticated(true);
/*
* 导入一次角色权限,并且把权限set到User中,用于spring验证用户权限(getAuthorities方法)
*/
List
Set
for(UserRole ur:userRoles){
accesses.add(ur.getRole());
}
user.getOrg().getOrgName();
if(user.getOrg().getCertTypes()!=null) user.getOrg().getCertTypes().size();//延迟载入一下
user.setAccesses(accesses);
return user;
}
return null;
}
重写supports(Class extends Object> authentication)方法,authentication要
/**
* 如果此处验证不通过,是不会执行authentication方法的
*/
@Override
public boolean supports(Class extends Object> authentication) {
return authentication.equals(PassCardAuthenticationToken.class);
}
4.定义filter,实现AbstractAuthenticationProcessingFilter的attemptAuthentication方法,用于获取在登录页面传递过来的参数,spring默认只获取userName(j_username),password(j_username),而且实现UserDetailsService时只传递username
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.util.StringUtils;
import cn.edu.jszg.cert.user.UserLog;
import cn.edu.jszg.cert.user.UserLogService;
import cn.edu.jszg.cert.web.WebApplicationConfiguration;
import cn.edu.jszg.cert.web.controller.portal.auth.RemoteDataValidator;
import com.google.code.kaptcha.servlet.KaptchaServlet;
public class PasscardAuthenticationProcessingFilter extends
AbstractAuthenticationProcessingFilter {
private String successPage = "/home/admin/index";
private String failurePage = "/public/adminLoginEntry";
private boolean forward = false;
private boolean useVerifyCode=true;
private String certLoginUrl;
static Logger logger = Logger.getLogger(PasscardAuthenticationProcessingFilter.class);
private WebApplicationConfiguration config;
private UserLogService userLogService;
public void setConfig(WebApplicationConfiguration config) {
this.config = config;
}
/**
* 实现AbstractAuthenticationProcessingFilter的有参构造
* 没记错的话,相当于该filter的访问路径
*/
protected PasscardAuthenticationProcessingFilter() {
super("/adminLoginCheck");
}
public void setUseVerifyCode(boolean useVerifyCode) {
this.useVerifyCode = useVerifyCode;
}
public void setUserLogService(UserLogService userLogService) {
this.userLogService = userLogService;
}
public boolean validate(HttpServletRequest request) {
String userId = request.getParameter("username");
String md2 = request.getParameter("m");
String l = request.getParameter("l");
if (userId == null || md2 == null || l == null) {
return false;
}
long longTime = Long.parseLong(l);
if (longTime < new Date().getTime()) {
return false;
}
try {
String md1 = RemoteDataValidator.genExamMd5Digest(userId, longTime);
if (md1.equals(md2))
return true;
} catch (Exception e) {
//e.printStackTrace();
}
return false;
}
/**
* 可以通过request获取页面传递过来的参数,并且set到相应的token中
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException,
IOException, ServletException {
// logger.warn("-----------------start证书登录用户----------");
HttpSession s = request.getSession(true);
PassCardAuthenticationToken token = new PassCardAuthenticationToken();
String verifyCode = request.getParameter("verifyCode");
String userID = request.getParameter("username");
//....此处省略获取参数,并且验证、赋值的逻辑
Authentication auth = null;
try {
//此处调用getAuthenticationManager的authenticate方法,当supports方法返回true时执行authenticate方法
auth = this.getAuthenticationManager().authenticate(token);
//此处为登录成功后,相应的处理逻辑
if (auth == null || !auth.isAuthenticated()) {
s.setAttribute("__login_error", token.getErrCode());
} else {
s.removeAttribute("__login_error");
s.removeAttribute("__login_username");
s.removeAttribute("__cert_userid");
if( token.isEnablePasscard()) {
s.removeAttribute("__passcard_row1");
s.removeAttribute("__passcard_row2");
s.removeAttribute("__passcard_column1");
s.removeAttribute("__passcard_column2");
}
}
} catch (AuthenticationException e) {
s.setAttribute("__login_error", token.getErrCode());
throw e;
}
return auth;
}
public void setSuccessPage(String successPage) {
this.successPage = successPage;
}
public void setFailurePage(String failurePage) {
this.failurePage = failurePage;
}
public void setForward(boolean forward) {
this.forward = forward;
}
public void setCertLoginUrl(String certLoginUrl) {
this.certLoginUrl = certLoginUrl;
}
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
/*
*该处理器实现了AuthenticationSuccessHandler, AuthenticationFailureHandler
*用于处理登录成功或者失败后,跳转的界面
*/
AuthenticationResultHandler handler = new AuthenticationResultHandler();
handler.setForward(forward);
handler.setLoginFailurePage(failurePage);
handler.setLoginSuccessPage(successPage);
handler.setCertLoginUrl(certLoginUrl);
//设置父类中的处理器
this.setAuthenticationSuccessHandler(handler);
this.setAuthenticationFailureHandler(handler);
}
}
最后为spring-security配置文件中的配置,需要添加authentication-provider的引用,和filter的配置
还要在http中添加
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~