मैंने अपनी कस्टम त्रुटि प्रतिक्रिया वापस करने के लिए AuthenticationEntryPoint को ओवरराइड कर दिया है। UserDetailsService के एक कार्यान्वयन के अंदर मैं org.springframework.security.core.userdetails.User के enabled फ़ील्ड को असत्य के रूप में पास कर रहा हूं। इसलिए प्रत्येक उपयोगकर्ता अक्षम है (इसका परीक्षण करने के लिए)।
लेकिन DisabledException पाने के बजाय मुझे हमेशा InsufficientAuthenticationException : Full authentication is required to access this resource मिलता है।

इसलिए मैंने और अधिक डिबग किया और पाया कि AbstractUserDetailsAuthenticationProvider#authenticate सही अपवाद (DisabledException) फेंक रहा है, लेकिन कहीं न कहीं यह InsufficientAuthenticationException में परिवर्तित हो रहा है

SecurityConfig.java

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
@ComponentScan(basePackages = {"com.restapi.security"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;
    private final JwtConfigProperties jwtConfigProperties;
    private final Environment environment;

    public SecurityConfig(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, JwtConfigProperties jwtConfigProperties, Environment environment) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
        this.jwtConfigProperties = jwtConfigProperties;
        this.environment = environment;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean
    JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        final JwtAuthenticationFilter filter = new JwtAuthenticationFilter(authenticationManager(), jwtConfigProperties, environment);
        filter.setFilterProcessesUrl(URN_AUTH + URN_LOGIN);
        filter.setAuthenticationManager(authenticationManager());
        filter.setAuthenticationSuccessHandler(successHandler());
        return filter;
    }

    @Bean
    SimpleUrlAuthenticationSuccessHandler successHandler() {
        final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
        successHandler.setRedirectStrategy(new NoRedirectStrategy());
        return successHandler;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .cors()
                .and().authorizeRequests()
                .antMatchers(HttpMethod.GET,"** private **")
                .authenticated()
                .antMatchers(HttpMethod.GET, "**public routes here**").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilter(jwtAuthenticationFilter())
                .addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtConfigProperties))
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint())
                .and().formLogin().disable().rememberMe().disable()
                .httpBasic().disable()
                .logout().disable()
                .csrf().disable()
        ;
    }
}

बाकी प्रमाणीकरणEntryPoint.java

@Component(value = "restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
            throws IOException {
        String errorMessage;
        ErrorCode errorCode;

       // the authEx is always of type InsufficientAuthenticationException

        if (authEx.getClass().equals(BadCredentialsException.class)) {
            errorMessage = "Incorrect username or password";
            errorCode = ErrorCode.UN_AUTHORIZED;
        } else if (authEx.getClass().equals(DisabledException.class)) {
            errorMessage = "Account verification pending, kindly verify your email address. An email has already been sent to your registered email address. If you have trouble finding it, kindly check the spam folder as well.";
            errorCode = ErrorCode.DISABLED;
        } else {
            errorMessage = authEx.getMessage();
            errorCode = ErrorCode.UN_AUTHORIZED;
        }

        ObjectMapper mapper = new ObjectMapper();
        ApiErrorDto apiErrorDto = new ApiErrorDto();
        apiErrorDto.setTimestamp(System.currentTimeMillis());
        apiErrorDto.setStatus(HttpStatus.UNAUTHORIZED.value());
        apiErrorDto.setCode(errorCode.value());
        apiErrorDto.setType(errorCode.getReasonPhrase());
        apiErrorDto.setError(authEx.getClass().getSimpleName());
        apiErrorDto.setMessage(errorMessage);
        apiErrorDto.setPath(request.getRequestURI());

        response.setContentType(APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = response.getWriter();

        writer.println(mapper.writeValueAsString(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(apiErrorDto)));
        writer.flush();

    }

}

UserDetailsService.java

@Service("userDetailsService")
@Transactional
public class MyUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    public MyUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByEmail(username)
                .orElseThrow(() -> new ResourceNotFoundException("No account found for " + username));

        return buildAuthenticatedUser(user);
    }

    private org.springframework.security.core.userdetails.User buildAuthenticatedUser(User user) {
        String username = user.getEmail();
        String password = user.getPassword();
        String uniqueId = user.getUniqueId();
        int userId = user.getUserId();
        boolean enabled = false; //hardcoded for now

        CurrentUser authenticatedUser = new CurrentUser(username, password, enabled, true, true, true, Collections.emptyList());
        authenticatedUser.setUniqueId(uniqueId);
        authenticatedUser.setUserId(userId);

        return authenticatedUser;
    }

}


////

@Getter(value = AccessLevel.PACKAGE)
@Setter(value = AccessLevel.PACKAGE)
public class CurrentUser extends User {
    private static final long serialVersionUID = -9189146656953053184L;

    private Integer userId;
    private String uniqueId;

    protected CurrentUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
    }

}

मुझसे यहां क्या गलत हो रहा है? मैं इसे और अधिक कैसे डिबग कर सकता हूं?

0
TheCoder 7 अक्टूबर 2021, 23:14

1 उत्तर

सबसे बढ़िया उत्तर

AuthenticationEntryPoint का कस्टम कार्यान्वयन मूल प्रमाणीकरण के साथ अच्छा काम करता है। लेकिन अगर हम एक कस्टम प्रमाणीकरण फ़िल्टर लिखते हैं तो हमें उस फ़िल्टर में ही इस असफल लॉगिन को संभालने की आवश्यकता होती है। मेरे मामले में मैं UsernamePasswordAuthenticationFilter का विस्तार कर रहा हूं, इसलिए यहां असफल प्रमाणीकरण के लिए मेरा कार्यान्वयन है।

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;
    private final JwtConfigProperties jwtConfigProperties;
    private final Environment environment;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtConfigProperties jwtConfigProperties, Environment environment) {
        this.authenticationManager = authenticationManager;
        this.jwtConfigProperties = jwtConfigProperties;
        this.environment = environment;
    }

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

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) throws IOException {
        String errorMessage;
        ErrorCode errorCode;
        int httpStatus = HttpStatus.UNAUTHORIZED.value();
        if (authEx.getClass().equals(BadCredentialsException.class)) {
            errorMessage = "Incorrect username or password";
            errorCode = ErrorCode.UN_AUTHORIZED;
        } else if (authEx.getClass().equals(DisabledException.class)) {
            errorMessage = "Account verification pending, kindly verify your email address. An email has already been sent to your registered email address. If you have trouble finding it, kindly check the spam folder as well.";
            errorCode = ErrorCode.DISABLED;
            httpStatus = HttpStatus.FORBIDDEN.value();
        } else {
            errorMessage = authEx.getMessage();
            errorCode = ErrorCode.UN_AUTHORIZED;
        }

        ObjectMapper mapper = new ObjectMapper();
        ApiErrorDto apiErrorDto = new ApiErrorDto();
        apiErrorDto.setTimestamp(System.currentTimeMillis());
        apiErrorDto.setStatus(httpStatus);
        apiErrorDto.setCode(errorCode.value());
        apiErrorDto.setType(errorCode.getReasonPhrase());
        apiErrorDto.setError(authEx.getClass().getSimpleName());
        apiErrorDto.setMessage(errorMessage);
        apiErrorDto.setPath(request.getRequestURI());

        response.setContentType(APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = response.getWriter();

        writer.println(mapper.writeValueAsString(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(apiErrorDto)));
        writer.flush();
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) throws IOException, ServletException {
        //logic
        super.successfulAuthentication(request, response, chain, auth);
    }


    @Setter
    @Getter
    private static class LoginCredentials {
        private String username;
        private String password;
    }

}

यह अब AbstractUserDetailsAuthenticationProvider के अंदर फेंके गए वास्तविक अपवाद को फेंक देता है।

आशा है कि यह किसी की मदद करेगा।

0
TheCoder 8 अक्टूबर 2021, 12:55