Ответы пользователя по тегу Spring
  • Spring Security. Всплывающее окон авторизации, как прописать loginpage в security configuration?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Здравствуйте!
    Не совсем понятен ваш вопрос...

    Вот, сниппет конфигурации
    .and()
                            .formLogin()
                            .loginPage("/login")
                            .loginProcessingUrl("/login")
                            .usernameParameter("email")
                            .passwordParameter("password")
                            .defaultSuccessUrl("/dashboard")
                            .failureUrl("/login?logout")
                            .permitAll()


    при нажатии на вход всплывает окно и ссылка получается вида site.com/***?login

    Почему при нажатии модалки урл у вас изменяется? Разве, что вы сами изменяете его через js.
    ?login - это всего лишь request parameter
    Ответ написан
    4 комментария
  • Почему выводит ошибку 500 на сервере?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Timed out waiting for driver server to start.
    У вас драйвер не стартовал и произошел тайм-аут. Проверьте пути к chromedriver для начала
    Ответ написан
  • Почему при удалении строки в Spring Boot с помощью JPA выводит ошибку?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Здравствуйте!
    То что вы видите 403 ошибку (Forbidden) особо ни о чем не говорит.
    1) Если у вас подключен Spring Security, то проверьте может ли пользователь совершить нужный вам запрос. @PreAuthorize("hasAuthority('ADMIN')") Пользователь должен быть админом
    2) Проверьте правильно ли вы обрабатываете post || get запрос.
    3) В этом участке кода тоже ошибка. Вы сперва удаляете admrem, а потом пытаетесь сохранить удаленный admrem.
    for (AdminProductions admrem : rmadm){
                adminProductRepo.deleteById(admrem.getId());
                adminProductRepo.save(admrem);
            }


    Скорее всего это лишнее
    adminProductRepo.save(admrem);

    4) Тут тоже может быть проблема. Например, в camelCase.
    deleteByAdminproductname()
    Потому, что у вас сущность называется AdminProduct и поле name
    А структура запроса у вас:
    Adminproductname
    Тоже что-то не так...

    Ну в конце концов, поставьте точки остановки, включите дебаг и посмотрите какая ошибка прибегает на сервер. Может быть у вас запрос даже не доходит до контроллера из-за ошибки в Spring Security
    Ответ написан
    3 комментария
  • Почему используются настройки application.yaml при профиле 'dev'?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    Подозреваю, что вы в файле application.yaml не указали активный профиль, а ваша настройка возможно, что не подхватывает нужный yaml.
    Например, в properties я обычно делаю так:
    3 файла:
    application.properties
    application-development.properties
    application-production.properties

    Содержимое application.properties
    spring.profiles.active=development

    Содержимое application-development.properties
    Вся конфигурация нужная для разработки
    Ответ написан
    Комментировать
  • Как сделать infinite scroll в thymeleaf?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    thymeleaf ни причем.
    1) используйте какую-нибудь библиотеку для аякс пагинации (будь-то infinite scroll или load more и др.).
    Например, https://infinite-scroll.com/
    https://www.sitepoint.com/jquery-infinite-scrollin...
    https://github.com/brianmario/jquery-infinite-scroll
    2) далее реализуйте RestController, который выдает контент в json. Задействуйте Pageable для реализации пагинации
    3) По достижению области видимости контента отправляйте аякс запрос и инкрементируйте pageable чтобы получить следующую страницу.
    Ответ написан
    Комментировать
  • Как правильно подгружать ресурсы в Spring?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Еще простое решение -
    Создайте файл ApplicationProperties
    @Configuration
    @ConfigurationProperties(prefix = "application")
    public class AppProperties {
    
        @Getter
        @Setter
        private String baseUrl;
    
       
        @Getter
        @Setter
        private String uploadPath;
    
    }


    Добавьте нужные конфиги:
    application.base_url=http://localhost:7777
    application.upload_path=/home/admin/application.com/uploads

    Обратите внимание на названия параметров.
    Сперва в конфиге указываем префикс application (вы можете указать свой префикс).
    Далее base_url => baseUrl upload_path => uploadPath

    Далее в MyApplication (в точку входа приложения) добавьте аннотацию:

    @EnableConfigurationProperties({
            ApplicationProperties.class
    })
    public class MyApplication {
    }


    Теперь в любом компоненте Spring вы можете аутовайрить этот класс и при помощи геттеров получать нужные параметры.
    Вот, .пример:

    @Controller
    @RequiredArgsConstructor
    public class MyController {
    
    private final ApplicationProperties properties;
    
    // ... тут методы контроллера
    String uploadPath = properties.getUploadPath();
    //...
    
    }

    Источник - https://www.baeldung.com/properties-with-spring
    Ответ написан
    Комментировать
  • Почему при миграции БД через flyway sequence не обновляется?

    azerphoenix
    @azerphoenix Автор вопроса, куратор тега Java
    Java Software Engineer
    Найдено решение - Ссылка
    Ответ написан
    Комментировать
  • Почему не находит CSRF Token при отправке multipart/form-data в Spring?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Виден ли токен в исходном коде DOM внутри тега form?
    А при сабмите формы CSRF токен отправляется?

    Конфиги для multipartfile я прописываю в properties С проблемой почему null в CSRF при отправке формы не сталкиввлся.
    # File upload size
    spring.servlet.multipart.max-file-size=20MB
    spring.servlet.multipart.max-request-size=20MB
    spring.servlet.multipart.file-size-threshold=2KB
    # Uploads
    spring.servlet.multipart.enabled=true
    fl.upload_path=/home/phoenix/example.com/uploads
    Ответ написан
    Комментировать
  • Themeleaf: Как вывести на страницу сообщение по дефолту?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    Здравствуйте!
    Почему бы вам не восопльзоваться возможностями шаблонизатора?
    Вот, несколько вариантов решения вопроса:

    1) Использование тернарного оператора
    <span th:text="${address.city != null} ? ${address.city} : 'No data!'">City</span>


    2) Использование: if unless (аналог if else на java). Обратите внимание, что если city это строка, то можно использовать ==, а если объект, то нужно использовать eq
    <span th:if="${address.city} == null">Non data</span>
    <span th:unless="${address.city} != null" th:text="'Your city : ' + ${address.city}">Non data</span>


    Также обратите внимание, что вы можете "обеспечить защиту" приложение, если значение null используя оператор безопасной навигации ?. между address & city
    <span th:text="'Your city : ' + ${address?.city}">Non data</span>
    Ответ написан
    1 комментарий
  • Как разрешить пользователю редактировать только собсвенные комментарии?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    У меня есть несколько идей по решению данной задачи. Какая из них корректная не скажу, но тем не менее решит ваши задачи.

    Итак,
    Для начала создадим controllerAdvice

    @RequiredArgsConstructor
    @ControllerAdvice
    public class GlobalControllerAdvice {
    private final UserServiceImpl userServiceImpl;
    
    @ModelAttribute("currentUser")
        public User getUserProfile(
                @AuthenticationPrincipal UserDetails currentUser
        ) {
            if (currentUser != null)
                return (User) userServiceImpl.findUserByEmail(currentUser.getUsername());
            return null;
        }
    
    }


    Теперь, нам доступна переменная ${currentUser} (текущий авторизованный пользователь)

    Далее, допустим, что мы хотим на клиенте отобразить иконку редактирования по клику на которую пользователь сможет отредактировать комментарий.
    Когда вы в html (в шаблонизаторе) циклом выводите список комментариев, то просто сравните текущего авторизованного пользователя и автора каждого комментария. Если equals(), то показываем иконку редактирования, а если нет, то не показываем.

    Например, для thymeleaf
    <div class="comments" th:each="comment : ${comments}"> 
    <input th:if="${comment.author} eq ${currentUser}" type="button" value="Отредактировать комментарий"/>
    </div>

    Или вы можете вместо сравнения объектов сравнить их id и т.д.

    А если нужно проверить автора в методе контроллера, то получаем текущего пользователя:
    @GetMapping("/edit/{commentId}")
    public String editComment(
    @PathVariable("commentId") Long commentId,
    @AuthenticationPrincipal UserDetails currentUser,
    ) {
    User user = (User) userServiceImpl.findUserByEmail(currentUser.getUsername());
    /*
    Далее находим комментарий по его id. находим его автора и сравниваем с user.
    */
    
    }
    Ответ написан
    2 комментария
  • Почему вместо id отображается null в OneToMany & ManyToOne и как это устранить?

    azerphoenix
    @azerphoenix Автор вопроса, куратор тега Spring
    Java Software Engineer
    Пока решил следующим способом:

    В HbDestination добавил setter
    public void setZones(List<Zone> zones) {
            zones.forEach(zone -> zone.setDestination(this));
            this.zones = zones;
        }


    03.03.2021
    Дополню свой ответ. Необходимо выставить каскадные стили.
    Полезные ссылки:
    https://www.baeldung.com/jpa-many-to-many
    https://vladmihalcea.com/the-best-way-to-map-a-man...
    https://www.baeldung.com/hibernate-many-to-many
    Ответ написан
    Комментировать
  • Как найти строчку в БД по значению столбца?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    Серьезно, прочитайте документацию.
    Например,

    @Entity 
    public class Book {
    @id
    private Long bookId;
    private String bookName;
    }


    В репозитории
    @Repository
    public interface BookRepository extends JpaRepository<Book, Long> {
    
    Book findBookByBookName(String bookName); 
    // или можно вернуть Optional
    Optional<Book> findBookByBookName(String bookName); 
    
    }
    Ответ написан
  • Как показать уведомление с анимацией без перезагрузки встраницы после сохранения формы в Spring Boot?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    Здравствуйте!
    Вы все правильно предположили насчет Ajax & JS + CSS.
    Писать код с нуля не буду, а лишь расскажу вам этапы для достижения желаемого...
    1) Создайте метод, который на ajax запрос будет возвращать @ResponseBody, либо создайте контроллер и добавьте аннотацию @RestController
    2) Перед закрывающим тегом </body> добавьте js код для аякс.
    https://ruseller.com/jquery.php?id=11
    https://api.jquery.com/jquery.ajax/
    3) Далее в контроллере обрабатываете запрос и возвращаете String или например, Map<String, String> с нужными данными.
    4) В ajax на событие success показываете ваш блок с анимацией.

    Теперь, про саму анимацию...
    Анимацию можно сделать средствами CSS или JS.
    Вот, пример CSS
    Допустим вот, ваш блок и он по умолчанию скрыт
    <div class="notify" style="display:none;"></div>
    При success делаете этот блок show() (видимым).
    Показать - https://api.jquery.com/show/
    Скрыть - https://api.jquery.com/hide/

    Остается только добавить анимацию - @keyframes
    https://www.w3schools.com/css/css3_animations.asp

    Вот, готовые библиотеки -
    https://stephanwagner.me/jBox
    https://notifyjs.jpillora.com/
    Ответ написан
    1 комментарий
  • Почему изображения отображаются только после перезагрузки Intelij Idea?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    Здравствуйте!
    Вам не нужно перезапускать саму программу.
    Поступите следующим образом:

    После того, как добавите новую статику или внесете изменения в ваш проект, то для того, чтобы увидеть изменения сделайте Build project (ctrl + F9) или Rebuild Project.

    Чтобы самому не заморачиваться каждый раз нажимая на кнопку build / rebuild включите авто сборку.
    Для этого следуйте указаниям:
    1)
    SHIFT+Ctrl+A ->registry-> compiler.automake.allow.when.app.running

    2)
    file->settings->build,execution,deployment. Go to ->compiler->build project automatically.

    3) Добавьте расширение для браузера LiveReload.
    Тогда после внесения правок каждый раз будет происходить авто сборка
    https://stackoverflow.com/questions/33869606/intel...
    Ответ написан
  • Почему в Spring Security не работает accessDaniedHandler?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    accessDeniedHandler срабатывает только если я зологинился и пытаюсь зайти на существующую запрещенную страницу. Почему он не срабатывает если я разлогиненый и пытаюсь зайти на существующую запрещенную страницу и перекидывает на страницу логина?


    Вы сами ответили на свой же вопрос. Проходя по цепочке он сперва натыкается на проблему авторизации и выдает исключение Access Denied Exception.
    Т.е. сперва он проверяет antMatchers и видит, что запрошенный URl не указан вpermitAll(), проходя дальше по цепочке он натыкается на .anyRequest().authenticated() и отдает Access Denied Exception.

    К тому же у вас строка /error закомментирована. Возможно, что он пытается показать ошибку, но так как эта страница недоступна будучи неавторизованным, то сперва перекидывает на страницу авторизации...

    Попробуйте расскомментировать эту строку и проверьте работает ли.
    Если нет, то попробуйте создать такой контроллер:

    @Controller
    public class HttpErrorController implements ErrorController {
    
        private final MessageSource messageSource;
    
        @Autowired
        public HttpErrorController(MessageSource messageSource) {
            this.messageSource = messageSource;
        }
    
        @RequestMapping("/error")
        public String handleError(
                Locale locale,
                Model model,
                HttpServletRequest request,
                Exception ex
        ) {
    
            Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
    
            if (status != null) {
    
                int statusCode = Integer.valueOf(status.toString());
    
                Map<String, String> metaData = new HashMap<>();
    
                // 403
                if (statusCode == HttpStatus.FORBIDDEN.value()) {
    
                    //do somthing
                }
    
                // 404
                else if (statusCode == HttpStatus.NOT_FOUND.value()) {
                    //do somthing
                }
    
                // 405
                else if (statusCode == HttpStatus.NOT_FOUND.value()) {
                    //do somthing
                }
    
                // 500
                else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
                    //do somthing
                }
    
            }
    
    
            return "errors/httperrors";
        }
    
        @Override
        public String getErrorPath() {
            return "/error";
        }
    
    }
    Ответ написан
  • Как сделать логин для админа в Spring Security?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    Здравствуйте!
    Я реализовал это следующим образом:
    В данном случае у меня 2 кастомные формы входа:
    1) для админ панели /admin/login
    2) для пользователей /auth
    /**
     * Конфигурация для Spring Security
     */
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig {
    
        private static UserDetailsServiceImpl userDetailsService;
    
        @Autowired
        private UserDetailsServiceImpl userDetailsServiceImpl;
    
        @PostConstruct
        private void init() {
            userDetailsService = this.userDetailsServiceImpl;
        }
    
        @Bean
        public static PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        public DaoAuthenticationProvider authProvider() {
            CustomAuthenticationProvider authProvider = new CustomAuthenticationProvider();
            authProvider.setUserDetailsService(userDetailsService);
            authProvider.setPasswordEncoder(passwordEncoder());
            return authProvider;
        }
    
        // Конфигурация для backend
        @Configuration
        @Order(1)
        public static class BackendConfigurationAdapter extends WebSecurityConfigurerAdapter {
    
    
            @Autowired
            private CustomWebAuthenticationDetailsSource authenticationDetailsSource;
    
            public BackendConfigurationAdapter() {
                super();
            }
    
            @Override
            protected void configure(HttpSecurity http) throws Exception {
    
                http
                        .antMatcher("/admin/**")
                        .antMatcher("/admin/**/**")
                        .authorizeRequests()
                        .anyRequest()
                        .hasAuthority("ADMIN_PRIVILEGE")
                        /*.hasAuthority("ADMIN")*/
    
    
                        .and()
                            .formLogin()
                            .authenticationDetailsSource(authenticationDetailsSource)
                            .loginPage("/admin/login")
                            .loginProcessingUrl("/admin/login")
                            .usernameParameter("email")
                            .passwordParameter("password")
                            .defaultSuccessUrl("/admin/dashboard")
                            .failureUrl("/admin/login?authError")
                            .permitAll()
    
                        .and()
                            .rememberMe()
                            .rememberMeParameter("remember-me")
                            .tokenValiditySeconds(86400)
    
                        .and()
                            .logout()
                            .logoutRequestMatcher(new AntPathRequestMatcher("/admin/logout"))
                            .logoutSuccessUrl("/admin/login")
                            .deleteCookies("JSESSIONID")
    
                        .and()
                            .exceptionHandling()
                            .accessDeniedPage("/403")
    
                        .and()
                            .csrf()
                            .ignoringAntMatchers("/admin/**");
            }
    
            @Override
            public void configure(WebSecurity web) {
                web.ignoring().antMatchers(
    
                        // статика backend
                        "/backend/css/**",
                        "/backend/js/**",
                        "/backend/fonts/**",
                        "/backend/images/**",
                        "/backend/init/**"
    
                );
            }
    
    
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.userDetailsService(userDetailsService);
            }
    
        }
    
    
        // Конфигурация для frontend (Обычная авторизация и авторизация oauth2)
        @Configuration
        @Order(2)
        public static class FrontendConfigurationAdapter extends WebSecurityConfigurerAdapter {
    
            @Autowired
            private CustomWebAuthenticationDetailsSource authenticationDetailsSource;
    
            public FrontendConfigurationAdapter() {
                super();
            }
    
            protected void configure(HttpSecurity http) throws Exception {
    
                http
                        .authorizeRequests().mvcMatchers("/robots.txt").permitAll()
                        .antMatchers(
                                "/", "/auth", "/signup", "/restore", "/activation/**",
                                "/admin/login", "/admin_restore",
                                "/attachments/get/**",
                                "/sendMessage",
                                "/error",
                                "/page/**",
                                "/categories", "/categories/**",
                                "/terms/**", "/posts", "/posts/**"
                        ).permitAll()
                        .anyRequest().authenticated()
    
                        .and()
                            .formLogin()
                            .authenticationDetailsSource(authenticationDetailsSource)
                            .loginPage("/auth")
                            .loginProcessingUrl("/auth")
                            .usernameParameter("email")
                            .passwordParameter("password")
                            .defaultSuccessUrl("/")
                            .failureUrl("/auth?authError")
                            .permitAll()
    
                        .and()
                            .rememberMe()
                            .rememberMeParameter("remember-me")
                            .tokenValiditySeconds(86400)
    
                        .and()
                            .oauth2Login().defaultSuccessUrl("/")
    
                        .and()
                            .logout()
                            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                            .logoutSuccessUrl("/")
                            .deleteCookies("JSESSIONID")
    
                        .and()
                            .exceptionHandling()
                            .accessDeniedPage("/403")
    
                        .and()
                            .csrf()
    
                        .and()
                            .sessionManagement()
                            .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
    
                        .and()
                            .headers().frameOptions().sameOrigin();
                        
    
            }
    
            @Override
            public void configure(WebSecurity web) {
                web.ignoring().antMatchers(
                        "/frontend/css/**",
                        "/frontend/js/**",
                        "/frontend/fonts/**",
                        "/frontend/images/**",
                        "/frontend/lib/**",
                        "/frontend/vendor/**"
                );
            }
    
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.userDetailsService(userDetailsService);
            }
    
    
        }
    
    }


    Админа, как по мне, лучше создать при инициализации приложения. Вот, код, как это делаю я.

    @Component
    public class InitData implements CommandLineRunner {
    
    @Override
    public void run(String... args) throws Exception {
    if (userServiceImpl.usersCount() == 0)
                initUsers();
    }
    
    private void initUsers() {
    // создаем пользователей
    }
    }
    Ответ написан
    3 комментария
  • Upload war файла в TomCat, при попытке доступа ошибка 404?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Пробовали ли вы перезапустить приложение после того, как залили war'ник ?
    Также обязательно просмотрите логи сервера.
    Исключили ли вы ошибки связанные с contextPath? Например, если у вас ссылка в шаблон добавлена хардкодом href="/contact" , то при наличии context может не работать. И соответственно, например, через thymeleaf надо добавлять как th:href="@{/contact}"
    Причины 404 могут быть разные...
    Ответ написан
    Комментировать
  • Не получается подключить spring config-server к postgresql?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Reason: Failed to determine a suitable driver class

    Тут написано, что не найден нужный драйвер для работы с БД. Хотя в yml вы явно указали драйвер.

    Вот, похожие проблемы (исключите их) -
    https://stackoverflow.com/questions/51403991/sprin...
    https://stackoverflow.com/questions/50896450/sprin...
    https://stackoverflow.com/questions/33323837/datab...

    Если вам удается подключитсья к БД из IDEA, то значит проблема не на стороне сервера. Помню, у меня когда-то была проблема с postgresql из-за отсутствия доступа к порту
    Ответ написан
  • Что делать если не получается авторизоваться spring rest?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Не лучше ли для REST использовать JWT авторизацию?
    https://www.callicoder.com/spring-boot-spring-secu...
    https://github.com/hantsy/springboot-jwt-sample
    https://medium.com/@hantsy/protect-rest-apis-with-...
    https://auth0.com/blog/implementing-jwt-authentica...
    https://www.baeldung.com/spring-security-oauth-jwt
    На основании этих материалов в своем проекте я реализовал JWT авторизацию
    Ответ написан
    Комментировать
  • Как создать кастомные страницы для Access Denied Page и Error Whitelabel Page в Spring Security?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    Здравствуйте!
    Access denied (Forbidden) - 403 ошибка.
    Создайте controller или controllerAdvice для http ошибок.

    @Controller
    public class HttpErrorController implements ErrorController {
    
        private final MessageSource messageSource;
    
        @Autowired
        public HttpErrorController(MessageSource messageSource) {
            this.messageSource = messageSource;
        }
    
        @RequestMapping("/error")
        public String handleError(
                Locale locale,
                Model model,
                HttpServletRequest request,
                Exception ex
        ) {
    
            Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
    
            if (status != null) {
    
                int statusCode = Integer.valueOf(status.toString());
    
                Map<String, String> metaData = new HashMap<>();
    
                // 403
                if (statusCode == HttpStatus.FORBIDDEN.value()) {
    
                   // do somthing
                }
    
                // 404
                else if (statusCode == HttpStatus.NOT_FOUND.value()) {
                    // do somthing
                }
    
                // 405
                else if (statusCode == HttpStatus.NOT_FOUND.value()) {
                    // do somthing
                }
    
                // 500
                else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
                   // do somthing
                }
    
            }
    
    
            return "templates/errors/httperrors";
        }
    
        @Override
        public String getErrorPath() {
            return "/error";
        }
    
    }


    Чтобы отключить whitelibel page добавьте в properties
    # Disable Whitelabel Error Page
    server.error.whitelabel.enabled=false
    Ответ написан
    1 комментарий