Spring Security官方文档对Pre-Authentication是这样解释的:
There are situations where you want to use Spring Security for authorization, but the user has already been reliably authenticated by some external system prior to accessing the application. We refer to these situations as “pre-authenticated” scenarios.
这里面涉及到Spring Security中两个概念,认证(Authentication)和授权(Authorization)。有关这两个概念的介绍,网上可以搜索到其他相关资料,这里仅通俗易懂的解释一下:
Spring Security框架提供了认证和授权的功能,但是有可能只希望使用Spring Security的授权功能,而不使用它提供的认证功能,比如使用一些其他认证方式,那么就可以使用Pre-Authentication。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" 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.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <security:http entry-point-ref="http403ForbiddenEntryPoint"> <!-- 省略其他配置 --> <security:custom-filter position="PRE_AUTH_FILTER" ref="preauthFilter" /> </security:http> <bean id="http403ForbiddenEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" /> <bean id="preauthFilter" class="com.xxg.test.auth.PreauthFilter"> <property name="authenticationManager" ref="authenticationManager" /> </bean> <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider"> <property name="preAuthenticatedUserDetailsService"> <bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"> <property name="userDetailsService" ref="userDetailsService"/> </bean> </property> </bean> <bean id="userDetailsService" class="com.xxg.test.auth.UserDetailsServiceImpl" /> <security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="preauthAuthProvider" /> </security:authentication-manager> </beans>
由于不再使用Spring Security提供的默认的用户名密码登录认证,需要修改 entry-point-ref
为 Http403ForbiddenEntryPoint
,否则会出现异常:
No AuthenticationEntryPoint could be established. Please make sure you have a login mechanism configured through the namespace (such as form-login) or specify a custom AuthenticationEntryPoint with the ‘entry-point-ref’ attribute
配置Pre-Authentication的最主要的部分是需要添加一个 position="PRE_AUTH_FILTER"
的Filter,这个Filter继承抽象类 AbstractPreAuthenticatedProcessingFilter
:
public class PreauthFilter extends AbstractPreAuthenticatedProcessingFilter { /** * 重写,返回用户名,这个用户名是经过其他方式认证过 */ @Override protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { if (authenticated) { // 可以通过request获取当前认证过的用户名,比如通过参数、HTTP请求头或者Cookie获取token,再通过token调用第三方接口获取用户名 return "your_username"; } else { // 如果认证失败,可以抛出异常 throw new PreAuthenticatedCredentialsNotFoundException("认证失败"); } } /** * 这个方法一般情况下不需要重写,直接返回空字符串即可 */ @Override protected Object getPreAuthenticatedCredentials(HttpServletRequest request) { return ""; } }
另外还有个重点配置 userDetailsService
,这个是用于用户认证后的授权。这里需要一个 UserDetailsService
的实现类,来获取用户的所有权限。
public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 这里可以通过用户名获取对应的权限 Collection<GrantedAuthority> auths = new ArrayList<>(); auths.add(new SimpleGrantedAuthority("ROLE_USER")); auths.add(new SimpleGrantedAuthority("ROLE_SUPER_ADMIN")); User user = new User(username, "", auths); return user; } }
如果是普通的浏览器访问的Web,以上完成配置后,用户在浏览器上首次访问会调用 AbstractPreAuthenticatedProcessingFilter
的 getPreAuthenticatedPrincipal
以及 UserDetailsService
的 loadUserByUsername
方法来获取认证用户和授权,并将相关信息保存到Session中,后续的请求直接通过Session获取用户信息,不再重复调用这些方法。
而对于API接口来说,一般情况下不会使用Session来做会话控制,例如可能会通过token的方式。API接口相对来说每次接口访问都是无状态的,所以针对每次请求都需要重新认证和授权。这个时候可以设置 create-session="stateless"
来禁掉Spring Security使用Session:
<security:http entry-point-ref="http403ForbiddenEntryPoint" create-session="stateless"> <!-- 省略其他配置 --> <security:custom-filter position="PRE_AUTH_FILTER" ref="preauthFilter" /> </security:http>