提问者:小点点

Spring安全登录中增加额外用户需求,处理各种异常


我是Spring Security的新手,我已经使用JWT为我的应用程序实现了基本的用户登录功能。除了在登录时检查用户名和密码之外,我还想添加其他参数,例如“帐户已验证”布尔条件,但我不确定在哪里添加此要求。此外,如果“帐户已验证”条件为假,我需要返回403禁止响应状态消息,如果根本找不到用户名密码组合,则返回不同的响应状态消息。这是我目前拥有的代码,它正确处理现有用户的登录(不检查“帐户已验证”条件),并在找到用户时始终抛出401。任何反馈都会有所帮助。

网络安全配置r适配器

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final ApplicationUserDetailsService applicationUserDetailsService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public WebSecurityConfig(ApplicationUserDetailsService userDetailsService) {
        this.applicationUserDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
          .cors()
          .and()
          .csrf()
          .disable()
          .authorizeRequests()
          .antMatchers("/**")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .addFilter(new AuthenticationFilter(authenticationManager()))
          .addFilter(new AuthorizationFilter(authenticationManager()))
          .sessionManagement()
          .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    public PasswordEncoder encoder() {
        return this.bCryptPasswordEncoder;
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(applicationUserDetailsService)
                .passwordEncoder(bCryptPasswordEncoder);
    }
}

用户详情服务

public class ApplicationUserDetailsService implements UserDetailsService {

    private final ApplicationUserRepository applicationUserRepository;

    public ApplicationUserDetailsService(ApplicationUserRepository applicationUserRepository) {
        this.applicationUserRepository = applicationUserRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String nickname)
            throws UsernameNotFoundException, UserIsNotActiveException {
        Optional<ApplicationUser> applicationUser =
                applicationUserRepository.findByNickname(nickname);
        if (!applicationUser.isPresent()) {
            throw new UsernameNotFoundException(nickname);
        }

        return new User(
                applicationUser.get().getNickname(),
                applicationUser.get().getPassword(),
                emptyList());
    }
}

身份验证过滤器

public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private AuthenticationManager authenticationManager;

    public AuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException {
        try {
            ApplicationUser applicationUser =
                    new ObjectMapper().readValue(req.getInputStream(), ApplicationUser.class);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            applicationUser.getNickname(),
                            applicationUser.getPassword(),
                            new ArrayList<>()));

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(
            HttpServletRequest req,
            HttpServletResponse res,
            FilterChain chain,
            Authentication auth) {
        Date exp = new Date(System.currentTimeMillis() + EXPIRATION_TIME);

        Key key = Keys.hmacShaKeyFor(KEY.getBytes());
        Claims claims = Jwts.claims().setSubject(((User) auth.getPrincipal()).getUsername());
        String token =
                Jwts.builder()
                        .setClaims(claims)
                        .signWith(key, SignatureAlgorithm.HS512)
                        .setExpiration(exp)
                        .compact();
        res.addHeader("token", token);
    }
}

授权过滤器

public AuthorizationFilter(AuthenticationManager authManager) {
    super(authManager);
}

@Override
protected void doFilterInternal(
        HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    String header = request.getHeader(HEADER_NAME);

    if (header == null) {
        chain.doFilter(request, response);
        return;
    }

    UsernamePasswordAuthenticationToken authentication = authenticate(request);

    SecurityContextHolder.getContext().setAuthentication(authentication);
    chain.doFilter(request, response);
}

private UsernamePasswordAuthenticationToken authenticate(HttpServletRequest request) {
    String token = request.getHeader(HEADER_NAME);
    if (token != null) {
        Jws<Claims> user =
                Jwts.parserBuilder()
                        .setSigningKey(Keys.hmacShaKeyFor(KEY.getBytes()))
                        .build()
                        .parseClaimsJws(token);

        if (user != null) {
            return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
        } else {
            return null;
        }
    }
    return null;
}

应用用户

public class ApplicationUser {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    @Column(unique = true)
    String email;

    @Column(unique = true)
    String nickname;

    String biography;

    String password; // Hashed

    @Builder.Default boolean isActive = false;
}

共1个答案

匿名用户

接口UserDetailsService(由UserDetailsService返回)有一些实用程序方法可以帮助您。

当帐户未激活时,您可以从User详细信息#isEnable方法返回false,或者您也可以使用User详细信息#isAccount tNonLocked

然后,这些方法将在AbstractUserDetailsAuthenticationProvider$Default(前/后)AuthenticationChecks类上自动验证。

用户通过激活流程后,您可以将属性更改为true,它将允许用户进行身份验证。

提示:将logging.level.org.springframework.security=TRACE添加到您的application.properties以帮助调试。