SpringSecurity JWT + Spring MVC

Всем привет) делаю учебный проект, осваивала основы SpringSecurity, пришло время мигрировать на JWT токены. У меня есть TokenProvider, который создает, верифицирует и достает токены, возвращает авторизацию, есть JwtTokenFilter, который вытягивает токены, и есть ко всему этому конфигурация:

@Component
public class JwtTokenProvider {

    @Value("${jwt.token.secret}")
    private String secretKey;
    @Value("${jwt.token.expiration}")
    private long validityInMilliseconds;

    private final UserDetailsService userDetailsService;

    @Autowired
    public JwtTokenProvider(@Qualifier("JwtUserDetailsService") UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @PostConstruct
    public void init() {
        secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }

    public String createToken(String username, List<Role> roleList) {
        Claims claims = Jwts.claims().setSubject(username);
        claims.put("roles", getRolesNames(roleList));

        Date now = new Date();
        Date expiration = new Date(now.getTime() + validityInMilliseconds);

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    public Authentication getAuthentication(String token) {
        UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
        return new UsernamePasswordAuthenticationToken(userDetails,
                "", userDetails.getAuthorities());
    }

    public String getUsername(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
    }


    public String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer_")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    public boolean validateToken(String token) {

        try {
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);

            if (claimsJws.getBody().getExpiration().before(new Date())) {
                return false;
            }
        } catch (JwtException | IllegalArgumentException e) {
            throw new JwtTokenException("Token expired or invalid");
        }

        return true;
    }

    public List<String> getRolesNames(List<Role> roles) {
        return roles.stream()
                .map(Role::getName)
                .collect(Collectors.toList());
    }
}

public class JwtTokenFilter extends GenericFilterBean {

    private final JwtTokenProvider tokenProvider;

    public JwtTokenFilter(JwtTokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        String token = tokenProvider.resolveToken((HttpServletRequest) servletRequest);
        if (token != null && tokenProvider.validateToken(token)) {
            Authentication authentication = tokenProvider.getAuthentication(token);
            if (authentication != null) {
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
}
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    private final JwtTokenProvider tokenProvider;

    public JwtConfigurer(JwtTokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    public void configure(HttpSecurity builder) throws Exception {
        JwtTokenFilter tokenFilter = new JwtTokenFilter(tokenProvider);
        builder.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

Я столкнулась со следующей проблемой:

Я хочу сохранить свой MVC, чтобы по-прежнему можно было взаимодействовать с сервером через вьюхи. Как можно установить хедер при логине, как их вместе слепить? Мой старый конфиг (я в нем пока ничего не трогала):

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private UserDetailsServiceImpl service;

    @Autowired
    public void setService(UserDetailsServiceImpl service) {
        this.service = service;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/", "/register", "/like").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login").permitAll()
                .defaultSuccessUrl("/home")
                .loginProcessingUrl("/login")
                .failureUrl("/login-error")
                .and()
                .logout()
                .logoutSuccessUrl("/")
                .permitAll()
                .logoutUrl("/logout")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .and()
                .csrf().disable();
    }

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

    @Bean
    protected PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(service);
        return  daoAuthenticationProvider;
    }
}

Так же мне хочется сохранить следующий функционал - обратите внимание на строку .failureUrl("/login-error"), и контроллер который это обрабатывает:

@RequestMapping("/login-error")
    public String errorModifyPage(Model model) {
        model.addAttribute("error", true);
        return "login";
    }
}

------------------------------------------------------------

<form class="form-signin" th:action="@{/login}" method="post">
        <img class="mb-4" src="https://memepedia.ru/wp-content/uploads/2017/08/%D0%BC%D0%B5%D0%BC-%D1%81%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5-1.jpg" alt="" width="125" height="92">
        <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
        <label for="inputEmail" class="sr-only">Email address or nickname</label>
        <input type="text" name="username" id="inputEmail" class="form-control" placeholder="Email address or nickname" required="" autofocus="">
        <small class="text-danger" th:if="${error} == true">Invalid email or password!</small>
        <label for="password" class="sr-only">Password</label>
        <input type="password" name="password" id="password" class="form-control mt-3" placeholder="Password" required="">

        <button class="btn btn-lg btn-warning btn-block mt-3" type="button" onclick="watch()" id="eye">Show password <i class="far fa-eye"></i></button>
        <button class="btn btn-lg btn-warning btn-block mt-3" type="button" style="display:none" id="closed_eye" onclick="watch()">Hide password <i class="far fa-eye-slash"></i></button>
<!--        <div class="checkbox mb-3">-->
<!--            <label>-->
<!--                <input type="checkbox" value="remember-me"> Remember me-->
<!--            </label>-->
<!--        </div>-->
        <button class="btn btn-lg btn-primary btn-block mt-3" type="submit">Sign in</button>
        <a th:href="@{/register}" class="btn btn-lg btn-primary btn-block mt-3" >Sign up</a>
    </form>

Тоесть, на страничке html - если ты что-то не то ввел, тебе возвращается та же страничка, но только теперь маленьким красным шрифтом обращает твое внимание на invalid email or password

Как сделать внутренний рефакторинг, соединив JWT и MVC и в то же время ничего не сломать? Как можно задать хедер в MVC?


Ответы (0 шт):