使用Java Config的Spring Security自定义身份验证过滤器 [英] Spring Security custom authentication filter using Java Config
问题描述
我正在尝试使用基本Web应用程序中的Java配置来配置Spring Security,以使用URL请求参数中提供的加密令牌对外部Web服务进行身份验证。
我希望(我认为)有一个安全过滤器拦截来自Login Portal的请求(它们都进入/验证),过滤器将使用AuthenticationProvider来处理身份验证过程的业务逻辑。
登录门户 - >重定向'\ authenticate'(+令牌) - >将令牌验证回登录门户(WS) - >如果成功获得角色并设置用户。
我创建了一个过滤器..
@Component
public final class OEWebTokenFilter extends GenericFilterBean {
@Override
public void doFilter(final ServletRequest request,final ServletResponse response,final FilterChain chain)抛出IOException,ServletException {
if(请求instanceof HttpServletRequ) est){
OEToken token = extractToken(request);
//将令牌转储到安全上下文中(用于提取身份验证的提供者)
SecurityContextHolder.getContext()。setAuthentication(token);
}
}
chain.doFilter(request,response);
}
AuthenticationProvider ...
@Component
公共最终类OEWebTokenAuthenticationProvider实现AuthenticationProvider {
@Autowired
private WebTokenService webTokenService;
@Override
公共布尔支持(final Class<?> authentication){
return OEWebToken.class.isAssignableFrom(authentication);
}
@Override
public身份验证身份验证(最终身份验证身份验证){
if(!(身份验证instanceof OEWebToken)){
抛出新的AuthenticationServiceException( 期待OEWebToken,得到+认证);
}
try {
//本地验证令牌
OEWebToken令牌=(OEWebToken)身份验证;
checkAccessToken(令牌);
//远程验证令牌
webTokenService.validateToken(令牌);
//从令牌获取用户信息
用户userFromToken = webTokenService.obtainUserInfo(令牌);
//从db
用户userFromDB = userDao.findByUserName(userFromToken.getUsername())获取用户;
//验证用户状态
checkUserStatus(userFromDB);
//用来自OE
updateUserInDb(userFromToken,userFromDB)的值更新ncss db;
//确定访问权限
列表< GrantedAuthority> roles = determineRoles(userFromDB);
//将帐户放入安全上下文(供控制器使用)
返回新的AuthenticatedAccount(userFromDB,roles);
} catch(AuthenticationException e){
throw e;
} catch(例外e){
//停止非AuthenticationExceptions。否则完全堆栈跟踪返回给请求者
抛出新的AuthenticationServiceException(发生内部错误);
}
}
我的Spring Security配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
公共类SecurityConfig扩展WebSecurityConfigurerAdapter {
@Autowired
OESettings oeSettings;
@Bean(name =oeAuthenticationService)
public AuthenticationService oeAuthenticationService()throws AuthenticationServiceException {
返回新的AuthenticationServiceImpl(new OEAuthenticationServiceImpl(),oeSettings.getAuthenticateUrl(),oeSettings。 getApplicationKey());
}
@Autowired
private OEWebTokenFilter tokenFilter;
@Autowired
private OEWebTokenAuthenticationProvider tokenAuthenticationProvider;
@Autowired
private OEWebTokenEntryPoint tokenEntryPoint;
@Bean(name =authenticationManager)
@Override
public AuthenticationManager authenticationManagerBean()throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(AuthenticationManagerBuilder auth)抛出异常{
auth.authenticationProvider(tokenAuthenticationProvider);
}
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(tokenFilter);
registrationBean.setEnabled(false);
返回registrationBean;
}
@Override
protected void configure(HttpSecurity http)throws Exception {
http.csrf()。disable()
.authorizeRequests()
.antMatchers(/ authenticate **)。permitAll()
.antMatchers(/ resources / **)。hasAuthority(ROLE_USER)
.antMatchers(/ home **)。hasAuthority(ROLE_USER)
.antMatchers(/ personSearch **)。hasAuthority(ROLE_ADMIN)
// Spring Boot执行器端点
.antMatchers( /autoconfig**\").hasAuthority(\"ROLE_ADMIN)
.antMatchers(/ beans **)。hasAuthority(ROLE_ADMIN)
.antMatchers(/ configprops **)。hasAuthority (ROLE_ADMIN)
.antMatchers(/ dump **)。hasAuthority(ROLE_ADMIN)
.antMatchers(/ env **)。hasAuthority(ROLE_ADMIN)
.antMatchers(/ health **)。hasAuthority(ROLE_ADMIN)
.antMatchers(/ info **)。hasAuthority(ROLE_ADMIN)
.antMatchers(/ mappings **)。hasAuthority(ROLE_ADMIN)
.antMatchers(/ metrics **)。hasAuthority(ROLE_ADMIN)
.antMatchers( /trace**\").hasAuthority(\"ROLE_ADMIN)
.and()
.addFilterBefore(tokenFilter,UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(tokenAuthenticationProvider)
.antMatcher( / authenticate / **)
.exceptionHandling()。authenticationQntryPoint(tokenEntryPoint)
.and()
.logout()。logoutSuccessUrl(oeSettings.getUrl());
}
}
我的问题是我在SpringConfig中配置过滤器类。我希望过滤器仅在请求用于/ authenticate URL时生效,我已将.antMatcher(/ authenticate / **)添加到过滤器配置中。
.and()
.addFilterBefore(tokenFilter,UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(tokenAuthenticationProvider)
.antMatcher(/ authenticate / **)
.exceptionHandling()。authenticationQntryPoint(tokenEntryPoint)
当我在所有其他URL中的此行不再受到保护时,我可以手动导航到/ home而不进行身份验证,删除该行并且/ home已通过身份验证。
我是否应声明仅适用于特定网址的过滤器?
如何在保持其他网址安全的同时实现此目的?
我已经通过在使用身份验证提供程序之前检查过滤器中的身份验证状态来解决我的问题.... / p>
配置
.and()
.addFilterBefore( TOK enFilter,UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(tokenAuthenticationProvider)
.exceptionHandling()。authenticationQntryPoint(tokenEntryPoint)
过滤器
@Override
public void doFilter(最终的ServletRequest请求,最终的ServletResponse响应, final FilterChain chain)
抛出IOException,ServletException {
logger.debug(此+从+ request.getRemoteHost()+收到身份验证请求到+ request.getLocalName()) ;
if(请求instanceof HttpServletRequest){
if(isAuthenticationRequired()){
//从头部提取令牌
OEWebToken令牌= extractToken(请求);
//将令牌转储到安全上下文中(用于提取身份验证的提供者)
SecurityContextHolder.getContext()。setAuthentication(token);
} else {
logger.debug(会话已经包含有效的身份验证 - 不再检查);
}
}
chain.doFilter(request,response);
}
private boolean isAuthenticationRequired(){
//显然过滤器必须自己检查。因此,请确保他们的会话中有正确的AuthenticatedAccount。
身份验证existingAuth = SecurityContextHolder.getContext()。getAuthentication();
if((existingAuth == null)||!existingAuth.isAuthenticated()){
return true;
}
if(!(existingAuth instanceof AuthenticatedAccount)){
return true;
}
//当前会话已经过身份验证
返回false;
}
I'm trying to configure Spring Security using Java config in a basic web application to authenticate against an external web service using an encrypted token provided in a URL request parameter.
I would like (I think) to have a security filter that intercepts requests from the Login Portal (they all go to /authenticate), the filter will use an AuthenticationProvider to process the bussiness logic of the authentication process.
Login Portal --> Redirect '\authenticate' (+ Token) --> Authenticate Token back to Login Portal (WS) --> If success get roles and setup user.
I have created a filter..
@Component
public final class OEWebTokenFilter extends GenericFilterBean {
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
OEToken token = extractToken(request);
// dump token into security context (for authentication-provider to pick up)
SecurityContextHolder.getContext().setAuthentication(token);
}
}
chain.doFilter(request, response);
}
An AuthenticationProvider...
@Component
public final class OEWebTokenAuthenticationProvider implements AuthenticationProvider {
@Autowired
private WebTokenService webTokenService;
@Override
public boolean supports(final Class<?> authentication) {
return OEWebToken.class.isAssignableFrom(authentication);
}
@Override
public Authentication authenticate(final Authentication authentication) {
if (!(authentication instanceof OEWebToken)) {
throw new AuthenticationServiceException("expecting a OEWebToken, got " + authentication);
}
try {
// validate token locally
OEWebToken token = (OEWebToken) authentication;
checkAccessToken(token);
// validate token remotely
webTokenService.validateToken(token);
// obtain user info from the token
User userFromToken = webTokenService.obtainUserInfo(token);
// obtain the user from the db
User userFromDB = userDao.findByUserName(userFromToken.getUsername());
// validate the user status
checkUserStatus(userFromDB);
// update ncss db with values from OE
updateUserInDb(userFromToken, userFromDB);
// determine access rights
List<GrantedAuthority> roles = determineRoles(userFromDB);
// put account into security context (for controllers to use)
return new AuthenticatedAccount(userFromDB, roles);
} catch (AuthenticationException e) {
throw e;
} catch (Exception e) {
// stop non-AuthenticationExceptions. otherwise full stacktraces returned to the requester
throw new AuthenticationServiceException("Internal error occurred");
}
}
And my Spring Security Config
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
OESettings oeSettings;
@Bean(name="oeAuthenticationService")
public AuthenticationService oeAuthenticationService() throws AuthenticationServiceException {
return new AuthenticationServiceImpl(new OEAuthenticationServiceImpl(), oeSettings.getAuthenticateUrl(), oeSettings.getApplicationKey());
}
@Autowired
private OEWebTokenFilter tokenFilter;
@Autowired
private OEWebTokenAuthenticationProvider tokenAuthenticationProvider;
@Autowired
private OEWebTokenEntryPoint tokenEntryPoint;
@Bean(name="authenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
@Bean
public FilterRegistrationBean filterRegistrationBean () {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(tokenFilter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate**").permitAll()
.antMatchers("/resources/**").hasAuthority("ROLE_USER")
.antMatchers("/home**").hasAuthority("ROLE_USER")
.antMatchers("/personSearch**").hasAuthority("ROLE_ADMIN")
// Spring Boot actuator endpoints
.antMatchers("/autoconfig**").hasAuthority("ROLE_ADMIN")
.antMatchers("/beans**").hasAuthority("ROLE_ADMIN")
.antMatchers("/configprops**").hasAuthority("ROLE_ADMIN")
.antMatchers("/dump**").hasAuthority("ROLE_ADMIN")
.antMatchers("/env**").hasAuthority("ROLE_ADMIN")
.antMatchers("/health**").hasAuthority("ROLE_ADMIN")
.antMatchers("/info**").hasAuthority("ROLE_ADMIN")
.antMatchers("/mappings**").hasAuthority("ROLE_ADMIN")
.antMatchers("/metrics**").hasAuthority("ROLE_ADMIN")
.antMatchers("/trace**").hasAuthority("ROLE_ADMIN")
.and()
.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(tokenAuthenticationProvider)
.antMatcher("/authenticate/**")
.exceptionHandling().authenticationEntryPoint(tokenEntryPoint)
.and()
.logout().logoutSuccessUrl(oeSettings.getUrl());
}
}
My problem is the configuration of the filter in my SpringConfig class. I want the filter to only come into effect when the request is for the /authenticate URL, I've added .antMatcher("/authenticate/**") to the filter configuration.
.and()
.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(tokenAuthenticationProvider)
.antMatcher("/authenticate/**")
.exceptionHandling().authenticationEntryPoint(tokenEntryPoint)
When I have this line in all other URLs are no longer secured, I can manually navigate to /home without authenticating, remove the line and /home is authenticated.
Should I be declaring a filter that is only applicable to a specific URL?
How can I implement this whilst maintaining the security of other URLs?
I've resolved my issue by performing a check on the authentication status in the filter before involking the authentication provider....
Config
.and()
.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(tokenAuthenticationProvider)
.exceptionHandling().authenticationEntryPoint(tokenEntryPoint)
Filter
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
logger.debug(this + "received authentication request from " + request.getRemoteHost() + " to " + request.getLocalName());
if (request instanceof HttpServletRequest) {
if (isAuthenticationRequired()) {
// extract token from header
OEWebToken token = extractToken(request);
// dump token into security context (for authentication-provider to pick up)
SecurityContextHolder.getContext().setAuthentication(token);
} else {
logger.debug("session already contained valid Authentication - not checking again");
}
}
chain.doFilter(request, response);
}
private boolean isAuthenticationRequired() {
// apparently filters have to check this themselves. So make sure they have a proper AuthenticatedAccount in their session.
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if ((existingAuth == null) || !existingAuth.isAuthenticated()) {
return true;
}
if (!(existingAuth instanceof AuthenticatedAccount)) {
return true;
}
// current session already authenticated
return false;
}
这篇关于使用Java Config的Spring Security自定义身份验证过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!