Профиль пользователя заблокирован сроком с 24 марта 2024 г. и навсегда по причине: систематические нарушения пункта 5.2 правил сервиса
Ответы пользователя по тегу Spring
  • Как в spring реализовать авторизацию по bearer токенам БЕЗ JWT?

    @My1Name
    В контексте Authentication, нужно смотреть в сторону ручной авторизации (автоматическая авторизация после регистрации). Там всё достаточно просто и понятно. Ну а если хочется изобрести что-то новенькое, то нужно читать документацию: https://docs.spring.io/spring-security/site/docs/4...
    Ответ написан
  • Сформировать вывод на страницу Thymeleaf?

    @My1Name
    Кладёте пользователей со всеми полями в List<Person> data передаёте на представление через model.addAttribute("Person", data);и выводите с помощью Thymeleaf итератора. Например так:
    <div th:each="user : ${Person}">
    <span th:text="${user.name}"></span>
    <span th:text="${user.date}"></span>
    <span th:text="${user.role}"></span>
    <span th:text="${user.description}"></span>
    </div>

    В данном примере, поля объекта Person с модификатором доступа - public.
    Как перебрать List<?> средствами Thymeleaf?
    Ответ написан
    1 комментарий
  • HTTP Status 500 Spring Thymeleaf, в чем причина ошибки?

    @My1Name
    В классе SpringConfig:
    @Bean
    public SpringResourceTemplateResolver templateResolver(){
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setApplicationContext(servletContext);
        templateResolver.setPrefix("/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setCacheable(false); // отключаем везде кэш на время разработки 
        return templateResolver;
    }
    @Bean
    public SpringTemplateEngine templateEngine(){
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        templateEngine.setEnableSpringELCompiler(true);
        return templateEngine;
    }
    @Bean
    public ThymeleafViewResolver viewResolver(){
    	ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    	viewResolver.setTemplateEngine(templateEngine());
    	viewResolver.setCharacterEncoding("UTF-8");
    	return viewResolver;
    }

    Вместо jakarta.servlet и thymeleaf-spring6 в poom.xml
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.thymeleaf</groupId>
    	<artifactId>thymeleaf-spring5</artifactId>
    </dependency>
    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>javax.servlet-api</artifactId>
    	<scope>provided</scope>
    </dependency>
    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>jstl</artifactId>
    </dependency>

    Класс MySpringMvcDispatcherServletInitializer можно (и нужно) удалить.

    Под Spring6 - Spring Security (если я не ошибаюсь) - ещё не работает.
    Ответ написан
  • В чем ошибка Spring MVC 404?

    @My1Name
    Переложите файлы "hello.hml" и "goodbye.html" на уровень выше: src/main/webapp/hello.html
    В таком случае ваш конфиг будет выглядеть так:
    templateResolver.setPrefix("/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode(TemplateMode.HTML);

    Контроллер:
    @Controller
    @RequestMapping("/") 
    public class FirstController {
    @GetMapping("/hello")
    public String helloPage() {
            return "hello";
        }
    }

    Страница доступна по адресу localhost:8080/hello где 8080 - это номер порта на котором работает ваш сервер.

    Узел WEB-INF не является частью общедоступного дерева документов приложения. Ни один файл, содержащийся в WEB-INF каталоге, не может быть передан контейнером непосредственно клиенту.
    https://stackoverflow.com/a/19786283/15454143
    Ответ написан
  • Зачем создавать слой с интерфейсами?

    @My1Name
    При проектировании, без интерфейсов, очень трудно представить весь объём работ для той или иной задачи. Интерфейсы по большому счёту, вкратце описывают алгоритм всей программы. Это шаблон проектирования, который позволяет при необходимости менять части программы (реализацию).

    изучая чужие проекты написанные на java spring boot

    Если долго гуглить уроки при изучении Spring-а, то можно найти много примеров, которые на самом деле ничего не делают, а посредством интерфейсов объясняют принцип работы. Именно для этого они и нужны.
    Ответ написан
    Комментировать
  • Как при удачной авторизации изменить кнопку Log In на кнопку Log Out и скрыть кнопку от обычных пользователей?

    @My1Name
    Вам нужен CustomAuthenticationProvider, а не CustomUserDetails. Там делегируются права (role) пользователям при аутентификации.

    Вот это вот лишнее:
    @PostMapping("/process_register")
        public String processRegister(User user) {
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            String encodedPassword = passwordEncoder.encode(user.getPassword());
            user.setPassword(encodedPassword);
            user.setRole("USER");
            userRepo.save(user);
            return "register_success";
        }

    Возможно имеет смысл в экспериментальных (в учебных) целях, но на практике это лишнее... PasswordEncoder достаточно добавить 1 раз через RegisterGlobalAuthentication в классе WebSecurityConfig; а также добавив соответствующий бин:
    @Bean
    public PasswordEncoder pass(){
    return new BCryptPasswordEncoder();
    }

    После регистрации, если она прошла успешно, обычно делается autoLogin через SecurityContextHolder и return "redirect:/index";

    Если вы хотите шифровать данные в БД, то имеет смысл вынести BCryptPasswordEncoder() в глобальную переменную (в final поле) на уровне класса - контроллера.

    Смысл PasswordEncoder() в Spring Security заключается в том, что на время сессии создаётся "хэш-отпечаток" (цифровая подпись) кроме прочей информации в памяти... Его в принципе невозможно декодировать не зная реальный пароль и правила преобразования. Поэтому без него Spring Security и не работает.

    Вот старая модель CHA-1 кодирования в соответствии со спецификацией Oracle к PasswordEncoder. А BCryptPasswordEncoder использует более новые и более продуманные алгоритмы.
    Ответ написан
    Комментировать
  • Как отправить два разных event в одном SseEmitter?

    @My1Name Автор вопроса
    Ответ банально прост (хотя я искал его 2 дня, и прямого ответа нигде не нашёл): Нужно создать два event в теле метода.
    SseEventBuilder event1 = SseEmitter....
    emitter.send(event1);
    SseEventBuilder event2 = SseEmitter....
    emitter.send(event2);

    На сайте Baeldung и др. ресурсах, есть только косвенный намёк на такое решение через ExecutorService... Нужно было заглянуть в javadoc SseEmitter - static interface extends ResponseBodyEmitter и можно было догадаться. https://www.baeldung.com/spring-server-sent-events
    Ответ написан
    Комментировать
  • Насколько Spring Security хорошо защищает твой бакэнд?

    @My1Name
    насколько хорошо spring security защищает

    В RESTful приложениях, по идее никаких проблем с безопасностью быть не может. Потому что любой запрос связан с сущностью, которая просто не будет построена если какая-то часть запроса не соответствует типу данных хотя бы одному из полей объекта (включая фильтры Security). См. Entity-Control-Boundary (Robustness diagram).

    Если у вас в системе есть контроллеры (например ajax), которые принимают запросы независимо от настроек фильтров Spring Security и независимо от сущности, то вопрос безопасности лежит на плечах разработчика.

    Spring-security - обеспечивает безопасный канал обмена данными. В каждом запросе клиент/сервер передаётся "пакетный набор" для идентификации пользователя. Например: IP адрес, hash-code сессии, крипто-пароль и др. Это обеспечивает безопасный канал связи. А обработка запросов (в том числе неправильных) - это задача разработчика.
    Ответ написан
  • Как обратиться в методе контролера к объекту запроса?

    @My1Name
    Как мне теперь обратиться к этому объекту в параметре метода контролера?

    Я не знаю что это за @InitBinder но я также не увидел, чтоб вы где-то создали объект "Product". Если WebDataBinder считается объектом, то вы его преобразовали в строку String str = binder.getTarget().toString()+ Далее вы создали MutablePropertyValues, что эквивалентно Map <String, String> или <String, Object> Короче говоря: вы нигде не создали объект Product чтоб к нему обращаться.
    Ответ написан
    Комментировать
  • Как защитить Spring приложение, отключив CSRF-токен?

    @My1Name Автор вопроса
    Учитывая тот факт, что у меня не банковское приложение и финансовых рисков особо никаких нет, я не вижу причин запрещать пользователям встраивать личные страницы (или др. страницы сайта) в iframe. Главная задача в таком случае: ͟з͟а͟п͟р͟е͟т͟и͟т͟ь͟ ͟а͟в͟т͟о͟р͟и͟з͟и͟р͟о͟в͟а͟н͟н͟ы͟е͟ ͟з͟а͟п͟р͟о͟с͟ы͟. Для этого я разместил в шапку (в header на всех страницах сайта) незамысловатый скрипт:
    const currentUrl = document.referrer;
    $.get('ajax/index', {ref:currentUrl});

    document.referrer - возвращает URL родительской страницы (которая загрузила iframe). Это работает, даже если родительский документ и iframe находятся на разных доменах. Как только начинается загрузка страницы, currentUrl отправляется в контроллер:
    private @ResponseBody void checkReferrer(Principal principal, 
    			HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		String currentUrl = req.getParameter("ref");
    		currentUrl = currentUrl.substring(currentUrl.indexOf("/")+2);
    		String localName = req.getServerName();
    		if (principal!=null && !currentUrl.startsWith(localName)) {
    			req.getSession().invalidate(); 
    			req.logout();
    			Cookie [] cookies = req.getCookies();
    			if(cookies!=null) {
    				for(Cookie c : cookies) {
    					c.setValue("");
    					c.setPath("/");
    					c.setMaxAge(0);
    					resp.addCookie(c);
    				}
    			}
    		}
    	}

    Контроллер проверяет переданный URL и сравнивает его с именем сервера. Далее, если переданный адрес не включает в себя имя сервера и это ͟а͟в͟т͟о͟р͟и͟з͟и͟р͟о͟в͟а͟н͟н͟ы͟й͟ ͟з͟а͟п͟р͟о͟с͟, то происходит выход с системы и удаление всех куки. В настройках Spring-Security:
    http.csrf()
    .disable().and()
    ....
    frameOptions().sameOrigin();

    Политика Same-Origin предотвращает доступ внешним скриптам к странице встроенной в iframe. А при отключении скриптов, любые запросы будут происходить от имени пользователя. То есть, програмно можно получить доступ только к своему аккаунту и только через главный вход (через iframe вход не получится). А если я что-то не учёл - критика в комментариях приветствуется.
    Ответ написан
  • Как сделать INSERT в две таблицы в одном запросе?

    @My1Name Автор вопроса
    Как сделать INSERT в две таблицы в одном запросе?

    Использовать batchUpdate. Например так:
    jdbcTemplate.batchUpdate(new String [] {
    "DELETE FROM statusChat WHERE id IN("+id1+", "+id2+") LIMIT 2",
    "DELETE FROM invites WHERE id="+id1+" AND idInviter="+id2
    });

    Для более сложных запросов с неизвестным количеством параметров, можно использовать BatchPreparedStatementSetter. Например:
    ArrayList <Long> listID;
    		String SQL = "DELETE FROM statusChat WHERE id IN(?";
    		for(int i=0; i<(listID.size()-1; i++)
    			SQL = SQL+",?"; SQL=SQL+")";
    		jdbcTemplate.batchUpdate(SQL, new BatchPreparedStatementSetter() {
    			@Override
    			public void setValues(PreparedStatement ps, int i) throws SQLException {
    				i=1;
    				for (int x=0; x<listID.size(); x++, i++) {
    					long id = listID.get(x);
    					ps.setLong(i, id));
    				}
    			}
    Ответ написан
    Комментировать
  • Как вернуть страницу с id в Spring?

    @My1Name
    Как вернуть страницу с id в Spring?

    В первую очередь нужно понять, что страница с id - это ссылка с параметрами. Ссылка - указывает на страницу, а параметры - на объект.

    В пост-методе контроллера "/routes/edit-all-sequence/{id}/" выделяет, как не соответствующую страницу MVC.

    Страница у вас одна. Меняются только значения связанные с объектом.

    Если вы используете redirect, то вместо Model используйте RedirectAttributes или ModelAndView. Тогда ваш код будет выглядеть следующим образом:model.addFlashAttribute("sequenceGapError", true); и будет автоматически распространяться на «выходной» FlashMap текущего запроса.

    Прежде чем делать перенаправление, попробуйте вернуть стандартную страницу:
    return "/routes/edit-all-sequence/"; По идее должна вернуться страница с параметрами {id} текущего запроса и, никакой redirect вам не нужен.
    Ответ написан
    Комментировать
  • Как вернуть ссылку с параметрами?

    @My1Name Автор вопроса
    Может кому-то поможет:
    @GetMapping("users/**")
    public String show(@RequestParam(value="id", required=false) String id){
    if (id==null)
    return "/";
    ........
    return "users";
    }

    Я искал решение, чтоб отображать "красивую ссылку", типа users/1234 или такую, которую пользователь мог бы выбрать сам (вместо 1234).

    Чтоб сформировать "красивую ссылку", url нужно получать с HttpServletRequest, и в таком случае параметры @RequestParam не нужны.
    @GetMapping("/publisher/**")
    	public String publisher(HttpServletRequest req, Principal principal, Model model){
    		String id = req.getRequestURL().toString();
    		id = id.substring(id.indexOf("/publisher")+10); // "/publisher" == 10
    		if (id.startsWith("/"))
    			id=id.substring(1);
    		if (principal==null && id.isBlank()) {
    			return "redirect:/";
    		}
    		else {
    			id = urlModelByOwner(principal, model, id);
    		}
    		if(!id.isBlank())
    		return "redirect:publisher"+id;
    	return "publisher";
    	}
    Ответ написан
  • Как средствами Thymeleaf вывести html переменную в JavaScript?

    @My1Name Автор вопроса
    <script th:inline="javascript">
    var htmlVariable = /*[#th:block th:utext="${htmlText}"/]*/;
    </script>
    Html текст нужно предварительно взять в 'одинарные' кавычки. Иначе JavaScript не будет работать.

    Более простой вариант:
    var htmlVariable = /*[[${htmlText}]]*/ 'value'; при этом 'value' будет заменено ${htmlText} содержанием. Однако, могут быть проблемы с UTF-8.

    Самый простой вариант:
    <script th:inline="javascript">
    var htmlVariable = '[(${htmlText})]';
    </script>
    Ответ написан
    Комментировать
  • Как установить в ответ http-код 412 и одновременно отменить выполнение запросов типа POST, PUT, DELETE?

    @My1Name
    Как установить в ответ http-код 412 и одновременно отменить выполнение запросов типа POST, PUT, DELETE?

    Написать класс - контролер для соответствующей страницы с двумя методами: Один с аннотацией @GetMapping Второй с аннотацией @RequestMapping("/") или @PostMapping для выше упомянутых случаев, и добавить в его параметры HttpServletResponse resp В теле метода:
    resp.setStatus(412); return "redirect:error_page.html";
    Можно сделать метод void или делать переадресацию.

    REST - это GET и POST запросы (обычный HTTP), а необходимые данные передаются в качестве параметров запроса. Так что PUT, DELETE и всё остальное, можно отнести к @PostMapping
    Ответ написан
    Комментировать
  • Почему не работает Thymeleaf аутентификация в Spring Security?

    @My1Name Автор вопроса
    Делаем по инструкции Thymeleaf и добавляем:
    templateEngine.addDialect(new SpringSecurityDialect());
    Класс конфигурации:
    @Configuration
    public class WebSecurityConfig implements WebMvcConfigurer {
    ........
    @Bean
    	public SpringResourceTemplateResolver templateResolver(){
    	    SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
    	    templateResolver.setApplicationContext(servletContext);
    	    templateResolver.setPrefix("/");
    	    templateResolver.setSuffix(".html");
    	    templateResolver.setTemplateMode(TemplateMode.HTML);
    	    templateResolver.setCacheable(false);
    	    return templateResolver;
    	}
    	@Bean
    	public SpringTemplateEngine templateEngine(){
    	    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    	    templateEngine.setTemplateResolver(templateResolver());
    	    templateEngine.setEnableSpringELCompiler(true);
    	    templateEngine.addDialect(new SpringSecurityDialect());
    	    return templateEngine;
    	}
    	@Bean
        public ThymeleafViewResolver viewResolver(){
            ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
            viewResolver.setTemplateEngine(templateEngine());
            return viewResolver;
        }
    }

    В pom.xml всё что связано с Thymeleaf и аутентификацией:
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.thymeleaf</groupId>
    	<artifactId>thymeleaf-spring5</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.thymeleaf.extras</groupId>
    	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
    </dependency>

    На странице index.html:
    <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
    <html xmlns:th="http://www.thymeleaf.org" 
    xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    Ответ написан
    Комментировать
  • Spring boot websocket через postman всегда возвращает 403, как так?

    @My1Name
    Попробуйте добавить бин:
    @Configuration
    @EnableWebSocketSecurity  
    public class WebSocketSecurityConfig {
    
        @Bean
        AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
            return AuthorityAuthorizationManager.hasRole("USER");
        }
    }

    Возможно вы отправляете запросы без авторизации...
    Ответ написан
    Комментировать
  • Spring Security: Как получить username и password?

    @My1Name Автор вопроса
    В Spring Security нельзя получить username и password привычными методами. После аутентификации запроса, данные обычно хранятся в локальном потоке SecurityContext, управляемом SecurityContextHolder. А чтоб достать username и password, нужно переопределить метод "Authentication" в классе AuthenticationProvider ("прослойка" между базой данных и клиентом, вшитая в Spring Security):
    @Component
    public class CustomAuthencationProvider implements AuthenticationProvider {
        @Autowired
        private CustomPersonRepository DAO;
        @Override
        public Authentication authenticate(Authentication authentication) 
                                              throws AuthenticationException {
            String userName = authentication.getName();
            String password = authentication.getCredentials().toString();
            Person myUser = DAO.findByUsername(userName);
            if (myUser == null) {
                throw new BadCredentialsException("Unknown user "+userName);
            }
            if (!password.equals(myUser.getPassword())) {
                throw new BadCredentialsException("Bad password");
            }
            UserDetails principal = User.builder()
                    .username(myUser.getLogin())
                    .password(myUser.getPassword())
                    .roles(myUser.getLogin())
                    .build();
            return new UsernamePasswordAuthenticationToken(
                    principal, password, principal.getAuthorities());
        }
        @Override
        public boolean supports(Class<?> authentication) {
            return authentication.equals(UsernamePasswordAuthenticationToken.class);
        }
    }

    Спринг работает на прямую с объектами "User" хранящимися в базе данных (или в локальном репозитории) и автоматизирует процесс обмена данными до уровня "запрос - ответ", а в контексте происходит аутентификация (кодирование/декодирование + проверка). То есть, хождение по сайту, подразумевает постоянный обмен пакетами данных сервера и клиента, между которыми существует прослойка "проверка" (аутентификация) реализованная в Spring Security. И если нам нужно что-то достать, то нужно реализовать методы спринга. В идеале, даже разработчик не должен видеть личные данные пользователя.

    Пароль в Spring Security по умолчанию закодирован на уровне контекста. И если мы хотим убрать кодирование (не рекомендуется), то нужно в классе конфигурации SecurityConfig implements WebMvcConfigurer указать соответствующий бин:
    @Bean
    	 public PasswordEncoder passwordEncoder() {
    		return NoOpPasswordEncoder.getInstance();
    	 }

    Пользовательская аутентификация в Spring Security
    Ответ написан
  • Как исправить проблему импорта классов после добавления JPA зависимости?

    @My1Name Автор вопроса
    Нужно добавить ещё одну зависимость:
    <dependency>  
    		<groupId>org.springframework.data</groupId>  
    		<artifactId>spring-data-jpa</artifactId>    
    		</dependency>

    Видимо произошёл какой-то конфликт зависимостей, от того все классы стали недоступны... Spring Data JPA предоставляет три репозитория: CrudRepository, PagingAndSortingRepository и JpaRepository. А Spring Boot Starter Data JPA позволяет подключаться приложению к реляционной базе данных.
    Ответ написан
    1 комментарий