Spring自定义身份验证过滤器和自定义jwtfilter

时间:2019-10-17 22:31:22

标签: java spring spring-boot security spring-security

我要实现的是使多租户应用程序工作区感知。我的意思是,除了用户名和密码之外,我还在验证工作区。

之前,我具有(有效的)常规身份验证(用户名和密码)和一个JWTFilter,它是一次的PerPerRequestFilter。

我做了什么?

  • 扩展了 UsernamePasswordAuthenticationToken :仅用于添加工作区
  • 扩展了 AbstractUserDetailsAuthenticationProvider :定义了我的customPasswordEncoder和customUserDetailsS​​ervice
  • 制作了 CustomUserDetailsS​​ervice :代替了loadByUsername,我创建了loadUserByWorkspaceAndUsername
  • 为新的扩展类配置 WebSecurity

结果总是未经授权的:(

我尝试了什么? 在调试代码时,永远不要传递CustomAuthenticationFilter,这就是我在那儿集中精力的原因。真的不知道我在做什么错。如果您需要更多信息,请大喊。

使用addFilter(authenticationFilter())替换UsernamePasswordAuthenticationFilter

 .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(authenticationFilter(), JwtFilter.class);
 .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
              .addFilterBefore(authenticationFilter(),UsernamePasswordAuthenticationFilter.class);
 .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);

一些代码。

CustomAuthenticationToken

public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken {

    private String workspace;

    public CustomAuthenticationToken(final Object principal,
                                     final Object credentials,
                                     final String workspace) {
        super(principal, credentials);
        this.workspace = workspace;
    }

    public CustomAuthenticationToken(final Object principal,
                                     final Object credentials,
                                     final String workspace, Collection<? extends GrantedAuthority> authorities) {
        super(principal, credentials, authorities);
        this.workspace = workspace;
        super.setAuthenticated(true);
    }

    public String getWorkspace() {
        return this.workspace;
    }
}

CustomAuthenticationFilter

public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "workspace";

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: "
                    + request.getMethod());
        }

        CustomAuthenticationToken authRequest = getAuthRequest(request);
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    private CustomAuthenticationToken getAuthRequest(HttpServletRequest request) {
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        String domain = obtainDomain(request);

        if (username == null) {
            username = "";
        }
        if (password == null) {
            password = "";
        }
        if (domain == null) {
            domain = "";
        }

        username = username.trim();
        return new CustomAuthenticationToken(username, password, domain);
    }

    private String obtainDomain(HttpServletRequest request) {
        return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY);
    }
}

CustomUserDetailsAuthenticationProvider

@Component
public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    /**
     * The plaintext password used to perform
     * PasswordEncoder#matches(CharSequence, String)}  on when the user is
     * not found to avoid SEC-2056.
     */
    private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
    private final PasswordEncoder customPasswordEncoder;
    private final CustomUserDetailsService customUserDetailsService;
    private String userNotFoundEncodedPassword;

    public CustomUserDetailsAuthenticationProvider(final PasswordEncoder customPasswordEncoder,
                                                   final CustomUserDetailsService customUserDetailsService) {
        this.customPasswordEncoder = customPasswordEncoder;
        this.customUserDetailsService = customUserDetailsService;
    }

    @Override
    protected void additionalAuthenticationChecks(final UserDetails userDetails,
                                                  final UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }

        final String presentedPassword = authentication.getCredentials().toString();
        if (!customPasswordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            logger.debug("Authentication failed: password does not match stored value");
            throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
    }

    @Override
    protected UserDetails retrieveUser(final String username,
                                       final UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        final CustomAuthenticationToken auth = (CustomAuthenticationToken) authentication;
        UserDetails loadedUser;

        try {
            loadedUser = this.customUserDetailsService.loadUserByWorkspaceAndUsername(auth.getWorkspace(), auth.getPrincipal().toString());
        } catch (UsernameNotFoundException notFound) {
            if (authentication.getCredentials() != null) {
                String presentedPassword = authentication.getCredentials().toString();
                customPasswordEncoder.matches(presentedPassword, userNotFoundEncodedPassword);
            }

            throw notFound;
        } catch (Exception repositoryProblem) {
            throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
        }

        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
        }

        return loadedUser;
    }

    @Override
    protected void doAfterPropertiesSet() throws Exception {
        Assert.notNull(this.customUserDetailsService, "A UserDetailsService must be set");
        this.userNotFoundEncodedPassword = this.customPasswordEncoder.encode(USER_NOT_FOUND_PASSWORD);
    }
}

CustomUserDetailsS​​erviceImpl

@Component
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {

    private static final Logger LOGGER = LoggerFactory.getLogger(com.cliwise.security.workspace.CustomUserDetailsServiceImpl.class);

    private final LoginAttemptService loginAttemptService;
    private final UserRepository userRepository;
    private final HttpServletRequest request;

    public CustomUserDetailsServiceImpl(LoginAttemptService loginAttemptService, UserRepository userRepository, HttpServletRequest request) {
        this.loginAttemptService = loginAttemptService;
        this.userRepository = userRepository;
        this.request = request;
    }

    @Override
    public UserDetails loadUserByWorkspaceAndUsername(String workspace, String username) throws UsernameNotFoundException {
        final User user = userRepository.findByUsernameOrEmailAndWorkspace(username, username, workspace)
                .orElseThrow(() -> new UserNotFoundException("User not found with username or email : " + username));

        return UserPrincipal.create(user);
    }
}

最后但并非最不重要的 WebSecurity

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    private final CustomAuthenticationEntryPoint unauthorizedHandler;
    private final CustomUserDetailsAuthenticationProvider customUserDetailsAuthenticationProvider;

    public WebSecurity(final CustomAuthenticationEntryPoint unauthorizedHandler,
                       final CustomUserDetailsAuthenticationProvider customUserDetailsAuthenticationProvider) {
        this.unauthorizedHandler = unauthorizedHandler;
        this.customUserDetailsAuthenticationProvider = customUserDetailsAuthenticationProvider;
    }

    @Override
    public void configure(final AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.authenticationProvider(customUserDetailsAuthenticationProvider);
    }

    @Bean
    public CustomAuthenticationFilter authenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }

    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler())
                .and()
                .addFilterBefore(jwtAuthenticationFilter(), CustomAuthenticationFilter.class);

        http
                .authorizeRequests()
                .antMatchers("/auth").permitAll()
                .anyRequest()
                .authenticated();
    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return new CustomAccessDeniedHandler();
    }

    @Bean
    public JwtFilter jwtAuthenticationFilter() {
        return new JwtFilter();
    }
}

提前感谢您的时间。

1 个答案:

答案 0 :(得分:0)

我的理解是,您在CustomUserDetailsAuthenticationProvider中面临问题。由于您正在扩展AbstractUserDetailsAuthenticationProver,因此您将获得

的默认实现。
public Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

查看其是否正确验证了身份验证对象,否则,您将不得不重写该方法并编写自己的实现。