详解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

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:security="http://www.springframework.org/schema/security"
  xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/security
     http://www.springframework.org/schema/security/spring-security-4.0.xsd"> 

  <!-- use-expressions:Spring 表达式语言配置访问控制 -->
  <security:http auto-config="true" use-expressions="false">
   <!-- 配置权限拦截,访问所有url,都需要用户登录,且拥有ROLE_USER权限 -->
    <security:intercept-url pattern="/**" access="ROLE_USER" /> 

  </security:http> 

  <security:authentication-manager alias="authenticationManager">
    <security:authentication-provider>
     <!-- 配置默认用户,用户名:admin 密码:123456 拥有权限:ROLE_USER -->
      <security:user-service>
        <security:user name="admin" password="123456"
          authorities="ROLE_USER" />
      </security:user-service>
    </security:authentication-provider> 

  </security:authentication-manager> 

</beans> 

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<AuthenticationProvider> providers集合中的各个AuthenticationProvider接口实现类中的authenticate(Authentication authentication)方法进行验证,由此可见,真正的验证逻辑是由各个AuthenticationProvider接口实现类来完成的。DaoAuthenticationProvider类是默认情况下注入的一个AuthenticationProvider接口实现类

4.provider的实现类在验证用户时,会调用userDetailsService的实现类的loadUserByUsername方法来获取用户信息,

首先spring-security配置文件

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/security
            http://www.springframework.org/schema/security/spring-security.xsd">
 <!-- use-expressions=”true” 需要使用表达式方式来写权限-->
 <http auto-config="true" use-expressions="false">
  <!--这是spring 提供的http/https信道安全的这个是重要的!你的请求信道是安全的!-->
  <!--
  释放用户登陆page 允许任何人访问该页面 ,IS_AUTHENTICATED_ANONYMOUSLY表示不拦截
  另一种不拦截资源的配置:<http pattern="/login.jsp" security="none">
  -->

  <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

   <!-- 配置用户正常访问page-->
   <intercept-url pattern="/**" access="ROLE_USER"/>

   <!-- 自定义用户登陆page default-target-url登陆成功跳转的page ,authentication-failure-url="/login.jsp?error=true"这里是登陆失败跳转的page-->
   <form-login login-page="/login.jsp" default-target-url="/jsp/index/main.jsp" authentication-failure-url="/login.jsp?error=true"/>
   <!-- 记住密码 -->
<!--   <remember-me key="elim" user-service-ref="securityManager"/> -->
 </http>

 <authentication-manager alias="authenticationManager">
 <!--
  authentication-provider 引用UserDetailsService实现类时使用user-service-ref属性,引用authentication实现类时,使用ref属性
  这两个属性的区别在于
    ref:直接将ref依赖的bean注入到AuthenticationProvider的providers集合中
    user-service-ref:定义DaoAuthenticationProvider的bean注入到AuthenticationProvider的providers集合中,
    并且DaoAuthenticationProvider的变量userDetailsService由user-service-ref依赖的bean注入。
 -->
 <authentication-provider user-service-ref="msecurityManager">
  <!-- 密码加密 -->
  <password-encoder ref="myPasswordEncoder"/>
 </authentication-provider>
 </authentication-manager>

 <!-- 实现UserDetailsService -->
 <beans:bean id="msecurityManager" class="com.ultrapower.me.util.security.support.SecurityManagerSupport"></beans:bean>
 <!-- 密码加密 -->
 <beans:bean id="myPasswordEncoder" class="com.ultrapower.me.util.security.MyPasswordEncoder"/>

</beans:beans>

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<User> users = getHibernateTemplate().find("FROM User user WHERE user.name = ? AND user.disabled = false", userName);
   log.info("SecurityManagerSupport.loadUserByUsername.userName:"+userName);

   User user =null;
   if("admin".equals(userName)){
     Set<Role> roles = new HashSet<Role>() ;
   Role role = new Role();
   role.setRoleid("ROLE_USER");
   role.setRoleName("ROLE_USER");

   Set<Resource> resources=new HashSet<Resource>() ;

   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<Role> roles;

 private Map<String, List<Resource>> roleResources;

 /**
 * The default constructor
 */
 public User() {

 }

 /**
 * Returns the authorites string
 *
 * eg.
 *  downpour --- ROLE_ADMIN,ROLE_USER
 *  robbin --- ROLE_ADMIN
 *
 * @return
 */
 public String getAuthoritiesString() {
   List<String> authorities = new ArrayList<String>();
   for(GrantedAuthority authority : this.getAuthorities()) {
     authorities.add(authority.getAuthority());
   }
   return StringUtils.join(authorities, ",");
 }

 @Override
 public Collection<? extends GrantedAuthority> getAuthorities() {
 // 根据自定义逻辑来返回用户权限,如果用户权限返回空或者和拦截路径对应权限不同,验证不通过
 if(!roles.isEmpty()){
  List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
  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<Role> getRoles() {
 return roles;
 }

 /**
 * @return the roleResources
 */
 public Map<String, List<Resource>> getRoleResources() {
 // init roleResources for the first time
 System.out.println("---------------------------------------------------");
 if(this.roleResources == null) {

  this.roleResources = new HashMap<String, List<Resource>>();

  for(Role role : this.roles) {
  String roleName = role.getRoleName();
  Set<Resource> resources = role.getResources();
  for(Resource resource : resources) {
   String key = roleName + "_" + resource.getType();
   if(!this.roleResources.containsKey(key)) {
   this.roleResources.put(key, new ArrayList<Resource>());
   }
   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<Role> roles) {
 this.roles = roles;
 }

 public String getAccount() {
 return account;
 }

 public void setAccount(String account) {
 this.account = account;
 }

 public void setRoleResources(Map<String, List<Resource>> roleResources) {
 this.roleResources = roleResources;
 }

}

3.实现动态过滤用户权限

在spring-security配置文件的http标签中添加如下配置

<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="securityInterceptor"/>

在spring-security配置文件中添加如下配置

<!-- 自定义拦截器 -->
 <beans:bean id="securityInterceptor" class="com.ultrapower.me.util.security.interceptor.SecurityInterceptor">
 <beans:property name="authenticationManager" ref="authenticationManager"/>
   <beans:property name="accessDecisionManager" ref="mesecurityAccessDecisionManager"/>
   <beans:property name="securityMetadataSource" ref="secureResourceFilterInvocationDefinitionSource" />
 </beans:bean>
<!-- 获取访问url对应的所有权限 -->
 <beans:bean id="secureResourceFilterInvocationDefinitionSource" class="com.ultrapower.me.util.security.interceptor.SecureResourceFilterInvocationDefinitionSource" />
<!-- 校验用户的权限是否足够 -->
 <beans:bean id="mesecurityAccessDecisionManager" class="com.ultrapower.me.util.security.interceptor.SecurityAccessDecisionManager" />

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<String, Collection<ConfigAttribute>> map = new HashMap<String, Collection<ConfigAttribute>>();

  /*
   * 初始化用户权限,为了简便操作没有从数据库获取
   * 实际操作可以从数据库中获取所有资源路径url所对应的权限
   */
  public void afterPropertiesSet() throws Exception {
    this.matcher = new AntPathMatcher();//用来匹配访问资源路径
    Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
    ConfigAttribute ca = new SecurityConfig("ROLE_USER");
    atts.add(ca);
    map.put("/jsp/index/main.jsp", atts);
    Collection<ConfigAttribute> attsno =new ArrayList<ConfigAttribute>();
    ConfigAttribute cano = new SecurityConfig("ROLE_NO");
    attsno.add(cano);
    map.put("/http://blog.csdn.net/u012367513/article/details/other.jsp", attsno);
  }

  @Override
 public Collection<ConfigAttribute> getAttributes(Object object)
  throws IllegalArgumentException {
 // TODO Auto-generated method stub
   FilterInvocation filterInvocation = (FilterInvocation) object;

   String requestURI = filterInvocation.getRequestUrl();
   //循环资源路径,当访问的Url和资源路径url匹配时,返回该Url所需要的权限
    for(Iterator<Map.Entry<String, Collection<ConfigAttribute>>> iter = map.entrySet().iterator(); iter.hasNext();) {
      Map.Entry<String, Collection<ConfigAttribute>> entry = iter.next();
      String url = entry.getKey();

      if(matcher.match(url, requestURI)) {
        return map.get(requestURI);
      }
    }

 return null;
 }

 @Override
 public Collection<ConfigAttribute> getAllConfigAttributes() {
 // 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<String, String> getUrlAuthorities(org.springframework.security.web.FilterInvocation filterInvocation) {
    ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
    return (Map<String, String>)servletContext.getAttribute("urlAuthorities");
  }

}

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<ConfigAttribute> configAttributes)
  throws AccessDeniedException, InsufficientAuthenticationException {
 // 对应url没有权限时,直接跳出方法
  if(configAttributes == null){
      return;
    } 

    Iterator<ConfigAttribute> ite=configAttributes.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<GrantedAuthority> getAuthorities() {
 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.getMD5ofStr(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<UserRole> userRoles=(List<UserRole>)this.getDao().executeQueryList("UserRole.listRoleByUserID", QueryCmdType.QUERY_NAME, -1, -1, user.getId());
  Set<GrantedAuthority> accesses=new HashSet<GrantedAuthority>();
  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的配置

<security:authentication-manager alias="authenticationManager">
 <!-- 注意,这里仅仅是系统默认的认证机制,请在正式系统中明确知道其功能再使用 -->
 <security:authentication-provider ref="acocunt_defaultAnthentiactionProvider"/>
 <security:authentication-provider ref="registrationService"/>
 <security:authentication-provider ref="enrollmentService"/>
 <security:authentication-provider ref="userService"/>
 </security:authentication-manager>
 <bean id="passcard_filter" class="cn.edu.jszg.cert.security.PasscardAuthenticationProcessingFilter">
 <property name="authenticationManager" ref="authenticationManager"/>
 <property name="useVerifyCode" value="true"/>
 <property name="failurePage" value="/portal/home/auth/"></property>
 <property name="config" ref="webAppConfig"/>
 <property name="userLogService" ref="userLogService" />
 <property name="certLoginUrl" value="${cert.login.url}"/>
 </bean>

还要在http中添加<security:custom-filter ref="passcard_filter" after="SECURITY_CONTEXT_FILTER"/>

到此这篇关于详解spring security四种实现方式的文章就介绍到这了,更多相关spring security 实现方式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • spring security动态配置url权限的2种实现方法

    缘起 标准的RABC, 权限需要支持动态配置,spring security默认是在代码里约定好权限,真实的业务场景通常需要可以支持动态配置角色访问权限,即在运行时去配置url对应的访问角色. 基于spring security,如何实现这个需求呢? 最简单的方法就是自定义一个Filter去完成权限判断,但这脱离了spring security框架,如何基于spring security优雅的实现呢? spring security 授权回顾 spring security 通过FilterCh

  • Spring security实现权限管理示例

    Spring security实现权限管理示例,具体如下: 1.配置文件 1.POM.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.o

  • Spring Security OAuth2实现使用JWT的示例代码

    1.概括 在博客中,我们将讨论如何让Spring Security OAuth2实现使用JSON Web Tokens. 2.Maven 配置 首先,我们需要在我们的pom.xml中添加spring-security-jwt依赖项. <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> &l

  • SpringBoot结合SpringSecurity实现图形验证码功能

    本文介绍了SpringBoot结合SpringSecurity实现图形验证码功能,分享给大家,具体如下: 生成图形验证码 根据随机数生成图片 将随机数存到Session中 将生成的图片写到接口的响应中 生成图形验证码的过程比较简单,和SpringSecurity也没有什么关系.所以就直接贴出代码了 根据随机数生成图片 /** * 生成图形验证码 * @param request * @return */ private ImageCode generate(ServletWebRequest r

  • SpringBoot + SpringSecurity 短信验证码登录功能实现

    实现原理 在之前的文章中,我们介绍了普通的帐号密码登录的方式: SpringBoot + Spring Security 基本使用及个性化登录配置. 但是现在还有一种常见的方式,就是直接通过手机短信验证码登录,这里就需要自己来做一些额外的工作了. 对SpringSecurity认证流程详解有一定了解的都知道,在帐号密码认证的过程中,涉及到了以下几个类:UsernamePasswordAuthenticationFilter(用于请求参数获取),UsernamePasswordAuthentica

  • Java中SpringSecurity密码错误5次锁定用户的实现方法

    Spring Security简介 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作. 下面看下实例代码: 第

  • Springboot+SpringSecurity+JWT实现用户登录和权限认证示例

    如今,互联网项目对于安全的要求越来越严格,这就是对后端开发提出了更多的要求,目前比较成熟的几种大家比较熟悉的模式,像RBAC 基于角色权限的验证,shiro框架专门用于处理权限方面的,另一个比较流行的后端框架是Spring-Security,该框架提供了一整套比较成熟,也很完整的机制用于处理各类场景下的可以基于权限,资源路径,以及授权方面的解决方案,部分模块支持定制化,而且在和oauth2.0进行了很好的无缝连接,在移动互联网的授权认证方面有很强的优势,具体的使用大家可以结合自己的业务场景进行选

  • 基于Spring Security的Oauth2授权实现方法

    前言 经过一段时间的学习Oauth2,在网上也借鉴学习了一些大牛的经验,推荐在学习的过程中多看几遍阮一峰的<理解OAuth 2.0>,经过对Oauth2的多种方式的实现,个人推荐Spring Security和Oauth2的实现是相对优雅的,理由如下: 1.相对于直接实现Oauth2,减少了很多代码量,也就减少的查找问题的成本. 2.通过调整配置文件,灵活配置Oauth相关配置. 3.通过结合路由组件(如zuul),更好的实现微服务权限控制扩展. Oauth2概述 oauth2根据使用场景不同

  • Spring security实现登陆和权限角色控制

     随笔简介 1.spring版本:4.3.2.RELEASE+spring security 版本:4.1.2.RELEASE(其它不做说明) 2.所展示内容全部用注解配置 3.springmvc已经配置好,不作说明 4.会涉及到springmvc,spel,el的东西,不熟悉的同学可以先去看一下这方面内容,特别是springmvc 首先想一下,登陆需要什么,最简单的情况下,用户名,密码,然后比对数据库,如果吻合就跳转到个人页面,否则回到登陆页面,并且提示用户名密码错误.这个过程中应该还带有权限

  • 详解spring security四种实现方式

    spring security实现方式大致可以分为这几种: 1.配置文件实现,只需要在配置文件中指定拦截的url所需要权限.配置userDetailsService指定用户名.密码.对应权限,就可以实现. 2.实现UserDetailsService,loadUserByUsername(String userName)方法,根据userName来实现自己的业务逻辑返回UserDetails的实现类,需要自定义User类实现UserDetails,比较重要的方法是getAuthorities()

  • 详解Spring的两种代理方式:JDK动态代理和CGLIB动态代理

    代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为"代理",所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用 A. 抽象主题角色 声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以是使用代理主题 B. 代理主题(Proxy)角色: 代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象:代理主题角

  • 详解Java的四种引用方式及其区别

    java内存管理分为内存分配和内存回收,都不需要程序员负责,垃圾回收的机制主要是看对象是否有引用指向该对象. java对象的引用包括 强引用,软引用,弱引用,虚引用 Java中提供这四种引用类型主要有两个目的: 第一是可以让程序员通过代码的方式决定某些对象的生命周期: 第二是有利于JVM进行垃圾回收. 下面来阐述一下这四种类型引用的概念: 1.强引用 是指创建一个对象并把这个对象赋给一个引用变量. 比如: Object object =new Object(); String str ="hel

  • 详解Spring Security 中的四种权限控制方式

    Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Security 提供的方式做授权,也可以自定义授权逻辑.一句话,你想怎么玩都可以! 今天松哥来和大家介绍一下 Spring Security 中四种常见的权限控制方式. 表达式控制 URL 路径权限 表达式控制方法权限 使用过滤注解 动态权限 四种方式,我们分别来看.  1.表达式控制 URL 路径权

  • 详解Spring Security中获取当前登录用户的详细信息的几种方法

    目录 在Bean中获取用户信息 在Controller中获取用户信息 通过 Interface 获取用户信息 在JSP页面中获取用户信息 在Bean中获取用户信息 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (!(authentication instanceof AnonymousAuthenticationToken)) { String currentU

  • 详解Spring Security如何在权限中使用通配符

    目录 前言 1. SpEL 2. 自定义权限该如何写 3. 权限通配符 4. TienChin 项目怎么做的 前言 小伙伴们知道,在 Shiro 中,默认是支持权限通配符的,例如系统用户有如下一些权限: system:user:add system:user:delete system:user:select system:user:update … 现在给用户授权的时候,我们可以像上面这样,一个权限一个权限的配置,也可以直接用通配符: system:user:* 这个通配符就表示拥有针对用户的

  • 详解Spring Security中权限注解的使用

    目录 1. 具体用法 2. SpEL 3. @PreAuthorize 最近有个小伙伴在微信群里问 Spring Security 权限注解的问题: 很多时候事情就是这么巧,松哥最近在做的 tienchin 也是基于注解来处理权限问题的,所以既然大家有这个问题,咱们就一块来聊聊这个话题. 当然一些基础的知识我就不讲了,对于 Spring Security 基本用法尚不熟悉的小伙伴,可在公众号后台回复 ss,有原创的系列教程. 1. 具体用法 先来看看 Spring Security 权限注解的具

  • 详解Python中四种关系图数据可视化的效果对比

    python关系图的可视化主要就是用来分析一堆数据中,每一条数据的节点之间的连接关系从而更好的分析出人物或其他场景中存在的关联关系. 这里使用的是networkx的python非标准库来测试效果展示,通过模拟出一组DataFrame数据实现四种关系图可视化. 其余还包含了pandas的数据分析模块以及matplotlib的画图模块. 若是没有安装这三个相关的非标准库使用pip的方式安装一下即可. pip install pandas -i https://pypi.tuna.tsinghua.e

  • 详解TCP的四种定时器

    详解TCP的四种定时器 在TCP协议中有的时候需要定期或者按照某个算法对某个事件进行触发,那么这个时候,TCP协议是使用定时器进行实现的.在TCP中,会有四种定时器: (1)重传定时器 (2)坚持定时器 (3)保活定时器 (4) 时间等待计时器 这四个定时器都有各自的具体作用. 一:重传定时器 重传定时器:为了控制丢失的报文段或丢弃的报文段,也就是对报文段确认的等待时间.当TCP发送报文段时,就创建这个特定报文段的重传计时器,可能发生两种情况:若在计时器超时之前收到对报文段的确认,则撤销计时器:

  • 详解Python的三种拷贝方式

    在练习列表的操作的时候我发现赋值之后的列表会随着被赋值的列表改变而改变,就像是C语言中用指向同一实际变量的指针进行操作一样.这是因为Python中有三种拷贝方式:浅拷贝.深拷贝和赋值拷贝. 赋值拷贝就像是定义新指针并指向了同一内存区域,对任意一个列表名进行操作,其他的也会变化. 深拷贝的作用是完全拷贝一个列表A并赋值给另一列表B.以下是深度拷贝与列表操作的样例.记得在使用深拷贝的时候要引入copy包. import copy #对列表的增删改 numbers_Ori = ['one', 'two

随机推荐