springSecurity的登录验证

springsecurity过滤指定url【.antMatchers(***).permitAll()】失效分析 springsercurity过滤指定url【antMatchers().permitAll】失效分析 阅读详情

原文:http://www.cnblogs.com/hzhuxin/archive/2011/12/14/2287363.html

springSecurity的登录验证是由org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter这个过滤器来完成的,在该类的父类AbstractAuthenticationProcessingFilter中有一个AuthenticationManager接口属性,验证工作主要是通过这个AuthenticationManager接口的实例来完成的。在默认情况下,springSecurity框架会把org.springframework.security.authentication.ProviderManager类的实例注入到该属性,

AuthenticationManager接口的相关类图如下:

UsernamePasswordAuthenticationFilter的验证过程如下:

1. 首先过滤器会调用自身的attemptAuthentication方法,从request中取出authentication, authentication是在org.springframework.security.web.context.SecurityContextPersistenceFilter过滤器中通过捕获用户提交的登录表单中的内容生成的一个org.springframework.security.core.Authentication接口实例.

2. 拿到authentication对象后,过滤器会调用ProviderManager类的authenticate方法,并传入该对象

3.ProviderManager类的authenticate方法再调用自身的doAuthentication方法,在doAuthentication方法中会调用类中的List<AuthenticationProvider> providers集合中的各个AuthenticationProvider接口实现类中的authenticate(Authentication authentication)方法进行验证,由此可见,真正的验证逻辑是由各个各个AuthenticationProvider接口实现类来完成的,DaoAuthenticationProvider类是默认情况下注入的一个AuthenticationProvider接口实现类

4.AuthenticationProvider接口通过UserDetailsService来获取用户信息

以下为时序图:


文章2:

工作需要,又弄起了权限的管理。虽然很早以前都了解过基于容器的权限实现方式,但是一直都觉得那东西太简陋了。后来使用liferay时发现它的权限系统的确做得很优秀,感觉这也可能是它做得最出色的地方吧。但是当时只停留在怎么使用及一些与其衔接的关系之上,并没有对其底层进行了解,新到现在的公司后,发现这一课还是得补上。但是令人惊讶的是,目前可用的选择并不多,甚至很少,最有名的当属spring security了,虽然很有名,但是关于这方面的资料并不是很多,应用示例就更少了。还好有中文的官方文档与http://www.family168.com/bbs/发布的简要教程,因此学起来不至于太困难。然后参照了一篇downpour写的spring security文章,因此勉强熟悉了spring security的应用开发,但是基本只停留在勉强会用的基础之上,而且还花了本人不少时间,在一个项目的运用中,自己更是差点没下得了台,惊出了一身冷汗。当时的感觉spring security就是个垃圾东西,运用很复杂,哪怕是做一个只拦截路径的权限系统,也要经过很多步骤。现在熟悉了它的一些流程后,虽然不知道这样的实现方式是否是最合理的,但是的确也有它的道理。现在利用放假期间,可以静下心来,理解一些以前让自己迷惑的东西了。downpour牛人的那篇文章讲得很好,以至于本人着实花了点时间才把它完全熟悉,当前自己以前对acegi并不熟悉。熟悉了那篇文章后,还是有些地方让自己不太理解,其中之一就是spring security是怎样完成用户角色权限验证的。下面就对这人问题进行简单的介绍: 

首先这篇文章是基于downpour那篇文章的,其地址为: 
http://www.iteye.com/topic/319965 

最先着手就是配置文件,这也是整个spring security最重要的入口点: 

Xml代码   收藏代码
  1. ............  
  2. <!--  处理国际化信息 -->  
  3.     <beans:bean id="authenticationManager"  
  4.         class="org.springframework.security.providers.ProviderManager">  
  5.         <beans:property name="messageSource" ref="messageSource" />  
  6.     </beans:bean>  
  7.       
  8.     <beans:bean id="messageSource"  
  9.         class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
  10.         <beans:property name="basename"  
  11.             value="classpath:org/springframework/security/messages_zh_CN" />  
  12.     </beans:bean>  
  13.   
  14.     <authentication-provider user-service-ref="securityManager">  
  15.         <password-encoder hash="md5" />  
  16.     </authentication-provider>  
  17.   
  18. <!-- AffirmativeBased表示只要有一个Voter通过权限要求,就可以访问 -->  
  19.     <beans:bean id="accessDecisionManager"  
  20.         class="org.springframework.security.vote.AffirmativeBased">  
  21.         <!-- 是否允许所有的投票者弃权,如果为false,表示如果所有的投票者弃权,就禁止访问 -->  
  22.         <beans:property name="allowIfAllAbstainDecisions"  
  23.             value="false" />  
  24.         <beans:property name="decisionVoters">  
  25.             <beans:list>  
  26. <!-- RoleVoter默认角色名称都要以ROLE_开头,否则不会被计入权限控制,如果要修改前缀,可以通过对rolePrefix属性进行修改 -->  
  27.                 <beans:bean class="org.springframework.security.vote.RoleVoter" />  
  28.                 <beans:bean class="org.springframework.security.vote.AuthenticatedVoter" />  
  29.             </beans:list>  
  30.         </beans:property>  
  31.     </beans:bean>  
  32.   
  33.     <beans:bean id="resourceSecurityInterceptor"  
  34.         class="org.springframework.security.intercept.web.FilterSecurityInterceptor">  
  35.         <beans:property name="authenticationManager" ref="authenticationManager" />  
  36.         <beans:property name="accessDecisionManager" ref="accessDecisionManager" />  
  37.         <beans:property name="objectDefinitionSource"  
  38.             ref="secureResourceFilterInvocationDefinitionSource" />  
  39.             <!-- 每次请求都进行检查,如果设为true,则只第一次检查,默认为true -->  
  40.         <beans:property name="observeOncePerRequest" value="false" />  
  41.         <custom-filter after="LAST" />  
  42.     </beans:bean>  
  43.   
  44.     <beans:bean id="secureResourceFilterInvocationDefinitionSource"  
  45.         class="com.javaeye.sample.security.interceptor.SecureResourceFilterInvocationDefinitionSource" />  
  46. ....  


上面的“accessDecisionManager”就是切入点,首先需要说明的是,在验证用户是否能通过验证时,spring security提供了三种策略,分别对应那个策略类: 
UnanimousBased.java 只要有一个Voter不能完全通过权限要求,就禁止访问。 
AffirmativeBased.java只要有一个Voter可以通过权限要求,就可以访问。 
ConsensusBased.java只要通过的Voter比禁止的Voter数目多就可以访问了。 

在此说一点,ConsensusBased这个类有点特别,如果通过的票数与禁止的票数相同怎么办? 
这个类有个allowIfEqualGrantedDeniedDecisions属性,默认为true,关键代码: 

Java代码   收藏代码
  1. if ((grant == deny) && (grant != 0)) {  
  2.             if (this.allowIfEqualGrantedDeniedDecisions) {  
  3.                 return;  
  4.             } else {  
  5.                 throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",  
  6.                         "Access is denied"));  
  7.             }  
  8.         }  


上面的代码表示如果allowIfEqualGrantedDeniedDecisions为true而且通过的票数不为0就授权访问。 

在提供好验证策略以后,继续关注其是怎么进行验证的。在上面的XML文件中,accessDescisionManager有个allowIfAllAbstainDecisions属性,这个属性的默认值为false,作用见注释处。现在主要关注的是其decisionVoters属性,中文的理解就是投票,RoleVoter.java 主要作用就是完成角色的投票验证,需要注意的是,它验证的角色,名称必须以ROLE_开头,当然这也可以通过配置文件改变,如: 

Xml代码   收藏代码
  1. <bean id="roleVoter" class="org.springframework.security.vote.RoleVoter">  
  2.     <property name="rolePrefix" value="AUTH_"/>  
  3. </bean>  


至于RoleVoter是如何完成验证的呆会再说,先回顾一下com.javaeye.sample.security.interceptor.SecureResourceFilterInvocationDefinitionSource: 

Java代码   收藏代码
  1. .....  
  2.     public ConfigAttributeDefinition getAttributes(Object filter) throws IllegalArgumentException {  
  3.           
  4.         FilterInvocation filterInvocation = (FilterInvocation) filter;  
  5.         String requestURI = filterInvocation.getRequestUrl();  
  6.         Map<String, String> urlAuthorities = this.getUrlAuthorities(filterInvocation);  
  7.           
  8.         String grantedAuthorities = null;  
  9.         for(Iterator<Map.Entry<String, String>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {  
  10.             Map.Entry<String, String> entry = iter.next();  
  11.               
  12.             //url表示从资源表取出的值,在这里代表的是相应的URL  
  13.             String url = entry.getKey();  
  14.               
  15.             //这段代码表示数据库内的需要验证的资源URL与当前请求的URL相匹配时进行验证  
  16.             if(urlMatcher.pathMatchesUrl(url, requestURI)) {  
  17.                 //grantedAuthorities表示每个资源对应的角色,如果有多个角色,则以','隔开  
  18.                 grantedAuthorities = entry.getValue();  
  19.                 break;  
  20.             }  
  21.         }  
  22.           
  23.         if(grantedAuthorities != null) {  
  24.             ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();  
  25.             configAttrEditor.setAsText(grantedAuthorities);  
  26.             return (ConfigAttributeDefinition) configAttrEditor.getValue();  
  27.         }  
  28.         return null;  
  29.     }  
  30. ....  


虽然不重要,但是还是有必要引用一下: 

引用

处于继承树顶端的AbstractSecurityInterceptor有三个实现类: 

FilterSecurityInterceptor,负责处理FilterInvocation,实现对URL资源的拦截。 
MethodSecurityInterceptor,负责处理MethodInvocation,实现对方法调用的拦截。 
AspectJSecurityInterceptor,负责处理JoinPoint,主要也是用于对方法调用的拦截。 

为了限制用户访问被保护资源,Spring Security提供了一套元数据,用于定义被保护资源的访问权限,这套元数据主要体现为ConfigAttribute和ConfigAttributeDefinition。每个ConfigAttribute中只包含一个字符串,而一个ConfigAttributeDefinition中可以包含多个ConfigAttribute。对于系统来说,每个被保护资源都将对应一个ConfigAttributeDefinition,这个ConfigAttributeDefinition中包含的多个ConfigAttribute就是访问该资源所需的权限。 

实际应用中,ConfigAttributeDefinition会保存在ObjectDefinitionSource中,这是一个主要接口,FilterSecurityInterceptor所需的DefaultFilterInvocationDefinitionSource和MethodSecurityInterceptor所需的MethodDefinitionAttributes都实现了这个接口。ObjectDefinitionSource可以看做是Spring Security中权限配置的源头,框架内部所有的验证组件都是从ObjectDefintionSource中获得数据,来对被保护资源进行权限控制的。 

为了从xml中将用户配置的访问权限转换成ObjectDefinitionSource类型的对象,Spring Security专门扩展了Spring中提供的PropertyEditor实现了ConfigAttributeEditor,它可以把以逗号分隔的一系列字符串转换成包含多个ConfigAttribute的ConfigAttributeDefintion对象。 

"ROLE_ADMIN,ROLE_USER" 

↓ 

ConfigAttributeDefinition 
  ConfigAttribute["ROLE_ADMIN"] 
  ConfigAttribute["ROLE_USER"] 
        
对于FilterSecurityInterceptor来说,最终生成的就是一个包含了url pattern和ConfigAttributeConfiguration的ObjectDefinitionSource。 


<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN,ROLE_USER" /> 

↓ 

                    ConfigAttributeDefinition 
"/admin.jsp"   →     ConfigAttribute["ROLE_ADMIN"] 
                      ConfigAttribute["ROLE_USER"] 
        
换而言之,无论我们将权限配置的原始数据保存在什么地方,只要最终可以将其转换为ObjectDefintionSource就可以提供给验证组件进行调用,实现权限控制。 



当时一直不明白这个getAttributes到底拿来做什么的。下面一步步进行追终,通过配置文件可知,这个类首先会到org.springframework.security.intercept.web.FilterSecurityInterceptor这个类中,这个类有个主要的方法: 

Java代码   收藏代码
  1. public void invoke(FilterInvocation fi) throws IOException, ServletException {  
  2.        if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)  
  3.            && observeOncePerRequest) {  
  4.            // filter already applied to this request and user wants us to observce  
  5.            // once-per-request handling, so don't re-do security checking  
  6.            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  
  7.        } else {  
  8.            // first time this request being called, so perform security checking  
  9.            if (fi.getRequest() != null) {  
  10.                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);  
  11.            }  
  12.   
  13.            InterceptorStatusToken token = super.beforeInvocation(fi);  
  14.   
  15.            try {  
  16.                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  
  17.            } finally {  
  18.                super.afterInvocation(token, null);  
  19.            }  
  20.        }  
  21.    }  


在此有两点需要说明,首先是observeOncePerRequest属性,一般情况下保持其默认的true,文档上有说: 

Java代码   收藏代码
  1. /***  
  2. Indicates whether once-per-request handling will be observed. By default this is <code>true</code>,meaning the <code>FilterSecurityInterceptor</code> will only execute once-per-request. Sometimes users may wishit to execute more than once per request, such as when JSP forwards are being used and filter security is desired on each included fragment of the HTTP request. 
  3. */  


其次我们需要关注的重点是FilterSecurityInterceptor的超类AbstractSecurityInterceptor的beforeInvocation方法,下面贴出这个类中我们最需要关注的代码: 

Java代码   收藏代码
  1. protected InterceptorStatusToken beforeInvocation(Object object) {  
  2.         Assert.notNull(object, "Object was null");  
  3.   
  4.         if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {  
  5.             throw new IllegalArgumentException("Security invocation attempted for object "  
  6.                     + object.getClass().getName()  
  7.                     + " but AbstractSecurityInterceptor only configured to support secure objects of type: "  
  8.                     + getSecureObjectClass());  
  9.         }  
  10.   
  11.         ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);  
  12.   
  13.         if (attr == null) {  
  14.             if (rejectPublicInvocations) {  
  15.                 throw new IllegalArgumentException(  
  16.                         "No public invocations are allowed via this AbstractSecurityInterceptor. "  
  17.                                 + "This indicates a configuration error because the "  
  18.                                 + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");  
  19.             }  
  20.   
  21.             if (logger.isDebugEnabled()) {  
  22.                 logger.debug("Public object - authentication not attempted");  
  23.             }  
  24.   
  25.             publishEvent(new PublicInvocationEvent(object));  
  26.   
  27.             return null; // no further work post-invocation  
  28.         }  
  29.   
  30.         if (logger.isDebugEnabled()) {  
  31.             logger.debug("Secure object: " + object + "; ConfigAttributes: " + attr);  
  32.         }  
  33.   
  34.         if (SecurityContextHolder.getContext().getAuthentication() == null) {  
  35.             credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",  
  36.                     "An Authentication object was not found in the SecurityContext"), object, attr);  
  37.         }  
  38.   
  39.         Authentication authenticated = authenticateIfRequired();  
  40.   
  41.         // Attempt authorization  
  42.         try {  
  43.             this.accessDecisionManager.decide(authenticated, object, attr);  
  44.         }  
  45.         catch (AccessDeniedException accessDeniedException) {  
  46.             AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,  
  47.                     accessDeniedException);  
  48.             publishEvent(event);  
  49.   
  50.             throw accessDeniedException;  
  51.         }  
  52.   
  53.         if (logger.isDebugEnabled()) {  
  54.             logger.debug("Authorization successful");  
  55.         }  
  56.   
  57.         AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);  
  58.         publishEvent(event);  
  59.   
  60.         // Attempt to run as a different user  
  61.         Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);  
  62.   
  63.         if (runAs == null) {  
  64.             if (logger.isDebugEnabled()) {  
  65.                 logger.debug("RunAsManager did not change Authentication object");  
  66.             }  
  67.   
  68.             // no further work post-invocation  
  69.             return new InterceptorStatusToken(authenticated, false, attr, object);  
  70.         } else {  
  71.             if (logger.isDebugEnabled()) {  
  72.                 logger.debug("Switching to RunAs Authentication: " + runAs);  
  73.             }  
  74.   
  75.             SecurityContextHolder.getContext().setAuthentication(runAs);  
  76.   
  77.             // revert to token.Authenticated post-invocation  
  78.             return new InterceptorStatusToken(authenticated, true, attr, object);  
  79.         }  
  80.     }  
  81.   
  82.     /** 
  83.      * Checks the current authentication token and passes it to the AuthenticationManager if 
  84.      * {@link org.springframework.security.Authentication#isAuthenticated()} returns false or the property 
  85.      * <tt>alwaysReauthenticate</tt> has been set to true. 
  86.      * 
  87.      * @return an authenticated <tt>Authentication</tt> object. 
  88.      */  
  89.     private Authentication authenticateIfRequired() {  
  90.         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();  
  91.   
  92.         if (authentication.isAuthenticated() && !alwaysReauthenticate) {  
  93.             if (logger.isDebugEnabled()) {  
  94.                 logger.debug("Previously Authenticated: " + authentication);  
  95.             }  
  96.   
  97.             return authentication;  
  98.         }  


上面的代码首先关注其中的一行代码: 

Java代码   收藏代码
  1. ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);  


不错,这行代码就是SecureResourceFilterInvocationDefinitionSource存在的主要目的,它主要提供URL与ROLE这两个东西,至于需要验证的用户来源,上面有句代码: 
Java代码   收藏代码
  1. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();  


众所周知,用户登陆成功后,可以通过: 

Java代码   收藏代码
  1. (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();  


上面的代码可以获取当前登陆的用户的基本信息,所以验证时需要它也很自然的。既然所需要的东西都具备了,下面就讲讲该怎么验证的问题了。在上面的AbstractSecurityInterceptor内,有句代码: 
Java代码   收藏代码
  1. this.accessDecisionManager.decide(authenticated, object, attr);  


上面这个accessDecisionManager就是最开始讲的那个accessDecisionManager,终于回到原来的问题上了,由于上面的配置文件中使用了AffirmativeBased投票策略,下面就直接进入此类的decide方法: 
Java代码   收藏代码
  1. public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)  
  2.         throws AccessDeniedException {  
  3.         Iterator iter = this.getDecisionVoters().iterator();  
  4.         int deny = 0;  
  5.   
  6.         while (iter.hasNext()) {  
  7.             AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();  
  8.             int result = voter.vote(authentication, object, config);  
  9.   
  10.             switch (result) {  
  11.             case AccessDecisionVoter.ACCESS_GRANTED:  
  12.                 return;  
  13.   
  14.             case AccessDecisionVoter.ACCESS_DENIED:  
  15.                 deny++;  
  16.   
  17.                 break;  
  18.   
  19.             default:  
  20.                 break;  
  21.             }  
  22.         }  
  23.   
  24.         if (deny > 0) {  
  25.             throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",  
  26.                     "Access is denied"));  
  27.         }  
  28.   
  29.         // To get this far, every AccessDecisionVoter abstained  
  30.         checkAllowIfAllAbstainDecisions();  
  31.     }  


这个方法主要有三个作用,第一作用就是完成投票,第二就是验证,从上面的switch与if语句可以看出,只要有一个投票者赞成,就直接返回,验证通过。如果没有一个投票者赞成(弃权)而且有人反对,deny++,到if(deny>0)时扔出异常,最后禁止访问。最后一句 

Java代码   收藏代码
  1. //到这步时,所有的投票者都弃权了  
  2. // To get this far, every AccessDecisionVoter abstained  
  3.         checkAllowIfAllAbstainDecisions();  


这就到了allowIfAllAbstainDecisions属性起作用的时候了。下面就来讲讲AffirmativeBased中用到的投票类RoleVoter,这个类主要工作就是完成投票工作,然后将结果反馈给AffirmativeBased,下面列出RoleVoter的代码: 

Java代码   收藏代码
  1. public class RoleVoter implements AccessDecisionVoter {  
  2.     //~ Instance fields ================================================================================================  
  3.   
  4.     private String rolePrefix = "ROLE_";  
  5.   
  6.     //~ Methods ========================================================================================================  
  7.   
  8.     public String getRolePrefix() {  
  9.         return rolePrefix;  
  10.     }  
  11.   
  12.     /** 
  13.      * Allows the default role prefix of <code>ROLE_</code> to be overridden. 
  14.      * May be set to an empty value, although this is usually not desirable. 
  15.      * 
  16.      * @param rolePrefix the new prefix 
  17.      */  
  18.     public void setRolePrefix(String rolePrefix) {  
  19.         this.rolePrefix = rolePrefix;  
  20.     }  
  21.   
  22.     public boolean supports(ConfigAttribute attribute) {  
  23.         if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {  
  24.             return true;  
  25.         }  
  26.         else {  
  27.             return false;  
  28.         }  
  29.     }  
  30.   
  31.     /** 
  32.      * This implementation supports any type of class, because it does not query 
  33.      * the presented secure object. 
  34.      * 
  35.      * @param clazz the secure object 
  36.      * 
  37.      * @return always <code>true</code> 
  38.      */  
  39.     public boolean supports(Class clazz) {  
  40.         return true;  
  41.     }  
  42.   
  43.     public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {  
  44.         int result = ACCESS_ABSTAIN;  
  45.         Iterator iter = config.getConfigAttributes().iterator();  
  46.         GrantedAuthority[] authorities = extractAuthorities(authentication);          
  47.   
  48.         while (iter.hasNext()) {  
  49.             ConfigAttribute attribute = (ConfigAttribute) iter.next();  
  50.   
  51.             if (this.supports(attribute)) {  
  52.                 result = ACCESS_DENIED;  
  53.   
  54.                 // Attempt to find a matching granted authority  
  55.                 for (int i = 0; i < authorities.length; i++) {  
  56.                     if (attribute.getAttribute().equals(authorities[i].getAuthority())) {  
  57.                         return ACCESS_GRANTED;  
  58.                     }  
  59.                 }  
  60.             }  
  61.         }  
  62.   
  63.         return result;  
  64.     }  
  65.       
  66.     GrantedAuthority[] extractAuthorities(Authentication authentication) {  
  67.         return authentication.getAuthorities();  
  68.     }  
  69. }  


这个类中最重要的代码就是这句: 

Java代码   收藏代码
  1. if (attribute.getAttribute().equals(authorities[i].getAuthority())) {  
  2.                         return ACCESS_GRANTED;  
  3.                     }  


这句代码的意思就是把SecureResourceFilterInvocationDefinitionSource传入的角色名称与SecurityContextHolder.getContext().getAuthentication()传入的用户所拥有的角色的角色名称相比较,如果相等则通过验证。 

在配置文件中还用到了一个投票类AuthenticatedVoter,这个类与RoleVoter属于同级,RoleVoter用来验证角色,那AutherticatedVoter又是用来干什么的呢? 
这个类完整代码: 

Java代码   收藏代码
  1. public class AuthenticatedVoter implements AccessDecisionVoter {  
  2.     //~ Static fields/initializers =====================================================================================  
  3.   
  4.     public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY";  
  5.     public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED";  
  6.     public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY";  
  7.     //~ Instance fields ================================================================================================  
  8.   
  9.     private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();  
  10.   
  11.     //~ Methods ========================================================================================================  
  12.   
  13.     private boolean isFullyAuthenticated(Authentication authentication) {  
  14.         return (!authenticationTrustResolver.isAnonymous(authentication)  
  15.         && !authenticationTrustResolver.isRememberMe(authentication));  
  16.     }  
  17.   
  18.     public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {  
  19.         Assert.notNull(authenticationTrustResolver, "AuthenticationTrustResolver cannot be set to null");  
  20.         this.authenticationTrustResolver = authenticationTrustResolver;  
  21.     }  
  22.   
  23.     public boolean supports(ConfigAttribute attribute) {  
  24.         if ((attribute.getAttribute() != null)  
  25.             && (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())  
  26.             || IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())  
  27.             || IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute()))) {  
  28.             return true;  
  29.         } else {  
  30.             return false;  
  31.         }  
  32.     }  
  33.   
  34.     /** 
  35.      * This implementation supports any type of class, because it does not query the presented secure object. 
  36.      * 
  37.      * @param clazz the secure object 
  38.      * 
  39.      * @return always <code>true</code> 
  40.      */  
  41.     public boolean supports(Class clazz) {  
  42.         return true;  
  43.     }  
  44.   
  45.     public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {  
  46.         int result = ACCESS_ABSTAIN;  
  47.         Iterator iter = config.getConfigAttributes().iterator();  
  48.   
  49.         while (iter.hasNext()) {  
  50.             ConfigAttribute attribute = (ConfigAttribute) iter.next();  
  51.   
  52.             if (this.supports(attribute)) {  
  53.                 result = ACCESS_DENIED;  
  54.   
  55.                 if (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) {  
  56.                     if (isFullyAuthenticated(authentication)) {  
  57.                         return ACCESS_GRANTED;  
  58.                     }  
  59.                 }  
  60.   
  61.                 if (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) {  
  62.                     if (authenticationTrustResolver.isRememberMe(authentication)  
  63.                         || isFullyAuthenticated(authentication)) {  
  64.                         return ACCESS_GRANTED;  
  65.                     }  
  66.                 }  
  67.   
  68.                 if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {  
  69.                     if (authenticationTrustResolver.isAnonymous(authentication) || isFullyAuthenticated(authentication)  
  70.                         || authenticationTrustResolver.isRememberMe(authentication)) {  
  71.                         return ACCESS_GRANTED;  
  72.                     }  
  73.                 }  
  74.             }  
  75.         }  
  76.   
  77.         return result;  
  78.     }  
  79. }  


作用见下面引用: 
引用

AuthenticatedVoter用于判断ConfigAttribute上是否拥有IS_AUTHENTICATED_FULLY,IS_AUTHENTICATED_REMEMBERED或IS_AUTHENTICATED_ANONYMOUSLY之类的配置。 
如果配置为IS_AUTHENTICATED_FULLY,那么只有AuthenticationTrustResolver的isAnonymous()和isRememberMe()都返回false时才能通过验证。 
如果配置为IS_AUTHENTICATED_REMEMBERED,那么会在AuthenticationTrustResolver的isAnonymous()返回false时通过验证。 
如果配置为IS_AUTHENTICATED_ANONYMOUSLY,就可以在AuthenticationTrustResolver的isAnonymous()和isRememberMe()两个方法返回任意值时都可以通过验证。 


其中上面引用中的ConfigAttribute就是指SecureResourceFilterInvocationDefinitionSource的getAttributes()方法中ConfigAttributeDefinition中的ConfigAttribute,downpour的文章只是传入了ROLE_USER,ROLE_ADMIN等内容,要想让AuthenticatedVoter有用武之地,可以传入 
IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED、IS_AUTHENTICATED_ANONYMOUSLY中的值。 

需要注意的是AffirmativeBased中遍历的投票者是要分先后的,也就是说RoleVoter在AuthenticatedVoter前面的话,会先进行RoleVoter验证,如果RoleVoter投票未通过,再进行了AuthenticatedVoter投票。 

这样spring security的验证流程就基本清楚了,当然这篇文章也还是有些地方讲得不完善,以事有时间再来修改。



SpringBoot入门到精通-JwtUser` (no Creators, like default construct, exist): cannot deserialize from Objec [ERROR] 2020-07-09 19:19:18,057 [http-nio-8889-exec-15] c.c.c.e.BusinessExceptionHandler - [192.168.60.80]:[/]:[/woportalapi/cuuser/auth/userinfo GET] org.springframework.security.authentication.InternalAuthenticationServiceException: Could not read JSON: 阅读详情

相关推荐

详解spring security 配置多个AuthenticationProvider

主要介绍了详解spring security 配置多个AuthenticationProvider ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

Java学习之错误记录(2)---org.springframework.security.authentication.InternalAuthenticationServiceException:

今天在配置Spring Security过程中,没有看懂下面语句的作用。 @Bean CustomUserService customUserService() { // 注册UserDetailsService 的bean return new CustomUserServiceImpl(); } 将原博主的代码: @Bean CustomUs...

u013062192的博客 4万+

AI编程工具选哪个更高效?开发者深度评测

无论是代码生成、错误修复,还是架构设计,AI都能提供强大的辅助能力。然而,市面上AI编程工具众多,如何选择最适合自己的?本文将从代码质量、响应速度、多语言支持、集成能力等多个维度,为你解析2025年最主流的几款AI编程工具,帮助你做出更高效的选择。根据2025年最新调研数据,全球超过85%的开发者正在使用AI编程工具辅助开发,相比2023年增长了近40%。合理利用AI,能让你的开发效率提升数倍,但真正的核心竞争力,仍然在于你的编程思维和解决问题的能力。:技术较强的开发者、重视数据隐私的企业。

1854

org.springframework.security.authentication.InternalAuthenticationServiceException

错误:org.springframework.security.authentication.InternalAuthenticationServiceException: Invalid bound statement (not found): com.fish.Mapper.UserMapper.selectUserByUsername 原因找不到xxxMapper.xml配置文件 解决:在配置文件中加入你的配置文件的地址 ...

weixin_51146698的博客 1万+

保姆级教程:用STM32标准库搞定TIM1的PWM输出(附完整代码和避坑点)

本文提供STM32F103高级定时器TIM1的PWM输出保姆级教程,涵盖从工程搭建到完整代码实现的详细步骤。重点解析TIM1作为高级定时器的特殊配置要点,包括时钟初始化、PWM通道设置和GPIO复用功能,并附常见问题排查指南和动态调整占空比的实用技巧,帮助开发者高效实现精准PWM控制。

weixin_30673611的博客 433

s9keaz128 UART Bootloader 单片机篇(上)

网络上充斥着各种各样的串口升级方案,基本都是基于Y-Mode协议下载。采用这种升级方案学习还是可以,用在产品上还是有点欠缺。如下载完成后,需要把时间发送到设备就不好搞了。如下这个设备返回的信息,最后一项就是固件更新时间。固件名称: KEA128编译时间固件版本: 1.0.3硬件版本: 1.0.3产品序列号功能名称: App固件更新时间串口升级演示视频。

一人科技 1795

SpringSecurity短信验证码登录

短信验证码登录 时下另一种非常常见的网站登录方式为手机短信验证码登录,但Spring Security默认只提供了账号密码的登录认证逻辑,所以要实现手机短信验证码登录认证功能,我们需要模仿Spring Security账号密码登录逻辑代码来实现一套自己的认证逻辑。 短信验证码生成 我们先定义一个短信验证码对象SmsCode : public class SmsCode { private String code; private LocalDateTime expireTime;

Curtisjia的博客 3443

基于FPGA进位链的TDC高精度延时链设计与实现

本文详细介绍了基于FPGA进位链的高精度TDC延时链设计与实现方法。通过利用FPGA的CARRY4硬件资源,构建了理论分辨率达13.25ps/级的延时链,并提供了Verilog代码实现、物理布局约束技巧以及温度补偿策略。文章还涵盖了标定方法、数据转换处理和系统验证方案,为激光测距、高能物理等需要精确时间测量的应用提供了实用解决方案。

weixin_28731223的博客 486

springSecurity实现登录验证

文章目录实现AbstractAuthenticationToken自定义验证对象定义登录filter实现AbstractAuthenticationProcessingFilter的attemptAuthentication方法自定义身份验证处理器定义认证成功处理器定义认证失败处理器配置登录过滤器到springSecurity 实现AbstractAuthenticationToken自定义验证对象 在SpringSecurity认证过程中,最核心的对象为Authentication,这个对象用于在认证过

剪刀手何金银的技术博客 5863

给大一新生的蓝桥杯EDA备赛规划:从电路分析到立创EDA,如何用一年时间高效备赛?

本文为大一新生提供了一份系统的蓝桥杯EDA备赛规划,从电路分析基础到立创EDA实战操作,分阶段指导如何高效备赛。内容涵盖理论学习、软件操作、PCB设计专项提升及冲刺优化策略,帮助学生在一年内全面提升电子设计能力,为竞赛做好充分准备。

weixin_31476015的博客 287

VSCode+Pyside6开发环境配置全攻略:从安装到实战UI设计

本文提供了一份详细的VSCode与Pyside6集成开发环境配置指南。从创建Python虚拟环境、安装必备VSCode插件,到深度配置PYQT Integration插件以打通Qt Designer与代码编译,最后通过一个待办事项应用实战演示了从UI设计到业务逻辑绑定的完整工作流,旨在帮助开发者建立高效、可维护的GUI开发环境。

qq_27496129的博客 980

SpringSecurity登录添加验证码

登录添加验证码是一个非常常见的需求,网上也有非常成熟的解决方案,其实,要是自己自定义登录实现这个并不难,但是如果需要在SpringSecurity框架中实现这个功能,还得...

啊啊啊啊2 3285

Android系统开机时间分析与优化指南

本文系统阐述了Android开机时间优化的全流程技术方案。首先详细解析了从电源键按下到桌面显示的完整启动流程及关键事件序列,然后介绍了秒表计时、日志分析和自动化脚本三种测量方法。重点从底层和上层两个维度提出了具体优化措施:底层包括编译器优化、内核驱动裁剪和日志精简;上层涵盖系统服务参数调整、应用预优化和功能组件精简。最后提供了验证工具和方法,并强调优化需要在性能、功耗和功能间取得平衡。通过这套系统化方案,可有效提升Android设备的开机速度与用户体验。

学无止境 352

SpringBoot 集成 SpringSecurity 验证码验证登录

<!-- 验证码生成的库 --> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>

一个对Java“一往情深”的后端程序员,对Java生态体系有着浓厚的兴趣与持续的研究。当我不在Java里“内卷”时,我也会探出头来,看看Web技术的广阔天地。希望通过这个博客,与所有同行交流切磋,共同进步。 978

高速信号设计中的关键测试项目与测试仪器详解

高速信号设计是一个多维度协同优化的系统工程。时域分析如同外科手术刀般精准定位物理缺陷,频域分析提供传输通道的频谱指纹,信号质量评估量化系统鲁棒性,接收端容限测试验证极端工况下的可靠性,协议一致性确保生态互操作性,而电源完整性则直指诸多信号问题的根源。

u014344410的博客 217

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

取消springsecurity默认的登录验证

取消springsecurity默认的登录验证 问题描述 解决方法一 方法二 问题描述 springboot 2.x,访问swagger-ui.html时,会自动跳转到springsecurity的login页,自定义过滤路径的拦截器无效。 解决方法一 修改依赖,项目因为使用了一些加密功能才引入springsecurity依赖,原依赖如下 <dependency> <groupId>org.springframework.boot<

1886

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

SpringBoot基于SpringSecurity表单登录和权限验证

一、简介 上篇介绍了一个自己做的管理系统,最近空闲的时间自己在继续做,把之前登录时候自定义的拦截器过滤器换成了基于SpringSecurity来做,其中遇到了很多坑,总结下,大家有遇到类似问题的话就当是为大家闭坑吧。 二、项目实现功能和成果展示 首先来看下登录界面:这是我输入的一个正确的信息,点击登录后SpringSecurity会根据你输入的用户名和密码去验证是否正确,如果正确的话就去你定...

润青 6万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

SpringSecurity猥琐扩展短信验证码登录

项目登录框架用的是SpringSecurity,现要求扩展短信验证码模式,常规方法是实现AbstractAuthenticationProcessingFilter,AuthenticationProvider,AbstractAuthenticationToken。注意OncePasswordEncoderProxy的创建传入的被代理BCryptPasswordEncoder,你原来用的是什么就用什么,不然已有用户是无法完成密码校对的!关于AccountType,这是我封装的类。

qq_43044376的博客 1788

基于SpringSecurity实现图片验证码登录功能

图片验证码登录验证1.验证码流程详解2.验证码生成3.验证码校验 1.验证码流程详解 验证码流程图解析: 客户端打开登陆页的时候就要发送一个生成图片验证码的请求 服务端接受请求,就要随机生成验证码图片,将图片验证码响应给前端页面,并且要将生成的验证码保存到session中,以便登录验证校验 客户端收到验证码图片后,填入表单信息后,发送登录请求 服务端在接受到前端传来的验证码参数,要先与session中的比对,如果相同,则响应正确,如果不匹配,则返回相应错误信息,如验证码不匹配 本次系统测试效果: 本次

xhnb的博客 1152

SpringSecurity之HttpBasic登录验证

目录 一、需求分析 二、环境搭建 三、添加HttpBasic登录认证 四、HttpBasic登录认证的默认行为 一、需求分析 1.1、客户端发送请求,请求会被SpringSecurity的过滤器链拦截,判断请求是否经过了认证和授权 ? 1.2、如果请求没有被认证,则跳转到登录页面,进行登录后放行之前的请求 1.3、如果请求已经被认证,则请求服务器中的资源都会放行 1.4、完成退出功能。退出后跳转到登录页面 二、环境搭建 2.1、创建 SpringBoot 项目,引入 web 、..

踏 浪的博客 1313

Java实现手机验证码登录和SpringSecurity权限控制

由于身份受限,只能基于模拟的方式进行验证码发送,而不能通过手机短信实现(开通需要认证资质或上线项目) 2、后端生产验证码并保存到Redis中 3、手机登录后端代码编写 3.1、Controller登录控制器编写 页面登录够可以查看浏览器的Cookie是否有保存到用户的手机号信息查看Redis可视化工具查看保存的用户登录信息系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。用户认证成功后,需要为用户授权,其实就是指定当前用户可以

我要记录学习博客 2525

SpringSecurity实现自定义登录认证、权限验证、鉴权

SpringSecurity实现自定义登录认证、权限验证、鉴权 Demo源码:https://github.com/ygsama/ipa 自定义登录认证的实现需要实现三个接口 UserDetails 用户类接口 UserDetailsService 查询用户密码的service 接口 AuthenticationProvider 为认证管理器AuthenticationManager 提供验证 自定义权限验证时也需要实现三个接口:

紫眸的博客 1万+

提交调用验证_Springboot基于SpringSecurity图片验证码登录

点击上方蓝色字体,选择“标星公众号”优质文章,第一时间送达 作者|大嘤熊来源 | urlify.cn/Q7JB3e66套java从入门到精通实战课程分享在前面的简单登录验证,我们简单整合了SpringSecurity的登录,可以通过自定义设置或者从数据库中读取用户权限类。接下来我们实现一些简单的验证码相关的登录验证。1、图片验证码登录其实这里和最初的登录验证没啥区别,只是多...

weixin_36078669的博客 283
上一篇: 关于漫画
下一篇: 2013全国各地火车票起售时间(放票时间最新公布)
贼船叔叔
博客等级 码龄16年 241粉丝 101原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值