मैंने अपनी कस्टम त्रुटि प्रतिक्रिया वापस करने के लिए 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);
}
}
मुझसे यहां क्या गलत हो रहा है? मैं इसे और अधिक कैसे डिबग कर सकता हूं?
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
के अंदर फेंके गए वास्तविक अपवाद को फेंक देता है।
आशा है कि यह किसी की मदद करेगा।
संबंधित सवाल
नए सवाल
spring-boot
स्प्रिंग बूट एक ऐसा ढांचा है, जो पूर्ण न्यूनतम उपद्रव के साथ आसानी से वसंत-संचालित, उत्पादन-ग्रेड अनुप्रयोगों और सेवाओं को बनाने की अनुमति देता है। यह स्प्रिंग प्लेटफ़ॉर्म के नए और अनुभवी उपयोगकर्ताओं के लिए काम करने के लिए डिज़ाइन किए गए स्प्रिंग प्लेटफ़ॉर्म के बारे में एक राय लेता है।