【问题标题】:Spring boot security - display customised authentication failed message in web-service callSpring Boot 安全性 - 在 Web 服务调用中显示自定义身份验证失败消息
【发布时间】:2019-07-17 04:24:24
【问题描述】:

当用户尝试使用已暂停、锁定或无效的帐户调用我的网络服务时,我试图显示自定义错误。

问题是,无论我尝试什么,都会不断返回相同的消息:“访问此资源需要完全身份验证”

我的 CustomUserDetailsS​​ervice 是这样的:

@Service
public class CustomUserDetailsService implements UserDetailsService {
    private static final Logger logger = LogManager.getLogger(CustomUserDetailsService.class);

    private @Autowired CredentialsServiceQuery credentials;
    private @Autowired MemberProfile memberProfile;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User.UserBuilder builder = null;
        try {
            boolean exists = credentials.checkUserExists(username);
            if (exists) {
                memberProfile = credentials.getUserInformation(username);

                builder = User.withUsername(username);
                builder.password(memberProfile.getPassword());
                builder.authorities(getGrantedAuthorities());
                logger.info("User exists: {}", username);
            } else {
                throw new UsernameNotFoundException(SpringSecurityMessageSource.getAccessor().getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", new Object[] {username}, "User credentials is wrong"));
            }
        } catch (Exception ex) {
            throw new UsernameNotFoundException(SpringSecurityMessageSource.getAccessor().getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", new Object[] {username}, "User credentials is wrong"));
            //throw new UsernameNotFoundException("An error occured while trying to find the username, " + username, ex);
        }
        return builder.build();
    }

    private List<GrantedAuthority> getGrantedAuthorities(){
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.clear();        
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));

        return authorities;
    }

}

我的安全配置有必要的方法调用:

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().httpBasic().and().cors().and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and().exceptionHandling()
                .authenticationEntryPoint(entryPoint);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/",
                "/swagger-ui.html",
                "/webjars/**",
                "/swagger-resources/**",
                "/v2/api-docs",
                "/info");
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
        configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
        UrlBasedCorsConfigurationSource source = new
                UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration.applyPermitDefaultValues());
        return source;
    }

    public AuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider impl = new DaoAuthenticationProvider();
        impl.setUserDetailsService(userDetailsService);
        impl.setPasswordEncoder(passwordEncoder);
        impl.setHideUserNotFoundExceptions(false);
        return impl;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
    }

最后,我的入口点:

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException, ServletException {
        String json = String.format("{\"errorcode\": \"%s\", \"message\": \"%s\"}", response.getStatus(), ex.getMessage());
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(json);
    }
}

我了解,在这种情况下,入口点负责编写在调用我的 Web 服务时提供错误凭据时传递的错误消息。所以我的问题是如何让入口点传递自定义消息,例如“用户 A 的帐户已锁定”?

如果入口点做不到,还有什么我可以做的吗?

【问题讨论】:

    标签: java spring-boot spring-security


    【解决方案1】:

    我不得不放弃尝试让 Spring Security 自动为入口点提供来自用户详细信息服务的异常消息的想法。

    因此,我改为使用自定义身份验证提供程序以获得更多控制权,并引入了一个名为 ErrorMessage 的自定义服务:

    @Getter
    @Setter
    @ToString
    @Service
    public class ErrorMessage {
        private String status;
        private String message;
    }
    

    @Getter、@Setter 和 @ToString 注释来自 lombok。他们做到了,所以我不必编写 setter 和 getter 以及 toString 方法。

    在我的自定义身份验证提供程序中,我只是这样设置错误消息:

    private @Autowired ErrorMessage errorMessage;    
    @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                try {
                    String username = authentication.getName();
                    String password = authentication.getCredentials().toString();
    
                    if (!credentials.checkUserExists(username)) {
                        //set something here
                    }
    
                    memberProfile = credentials.getUserInformation(username);
    
                    if (passwordEncoder.matches(password, memberProfile.getPassword())) {
                        logger.info("It matches - encoder!");
                        return new UsernamePasswordAuthenticationToken(username, password, getGrantedAuthorities());
                    } else {
                        //error message bean utilised here
                        errorMessage.setStatus("100");
                        errorMessage.setMessage(username + "'s password is incorrect");
    
                        throw new BadCredentialsException("The credentials provided are incorrect");
                    }
                } catch (Exception ex) {
                    throw new BadCredentialsException("The credentials provided are incorrect", ex);
                }
            }
    

    然后我以这种方式在入口点收到自定义错误消息:

    @Component
    public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
        private @Autowired ErrorMessage errorMessage;
    
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException, ServletException {
            //String json = String.format("{\"errorcode\": \"%s\", \"message\": \"%s\"}", response.getStatus(), ex.getMessage());
            String json = String.format("{\"errorcode\": \"%s\", \"message\": \"%s\"}", errorMessage.getStatus(), errorMessage.getMessage());
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write(json);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-06-25
      • 2017-04-09
      • 1970-01-01
      • 2016-10-12
      • 2016-01-20
      • 2019-09-22
      • 1970-01-01
      • 2012-08-15
      • 1970-01-01
      相关资源
      最近更新 更多