feat: add authorization
This commit is contained in:
parent
03fedef553
commit
5f25234962
BIN
media/profiles/2/images.jpeg
Normal file
BIN
media/profiles/2/images.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@ -1,9 +1,37 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<div>
|
||||
<h3>Charm <3</h3>
|
||||
<table>
|
||||
<tr class="hiddenRow">
|
||||
<td>
|
||||
<a href="/"><img src="content/app/img/heart.png" width="75"></a>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="/lang">
|
||||
<button type="submit" name="lang" value="ru">ru</button>
|
||||
<button type="submit" name="lang" value="en">en</button>
|
||||
<button type="submit" name="lang" value="ru" class="langButton">
|
||||
<img src="content/app/img/ru.png" class="langImg">
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="/lang">
|
||||
<button type="submit" name="lang" value="en" class="langButton">
|
||||
<img src="content/app/img/en.png" class="langImg">
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<c:if test="${sessionScope.userDetails == null}">
|
||||
<a href="/login"><img src="content/app/img/key.png" width="75"></a>
|
||||
</c:if>
|
||||
<c:if test="${sessionScope.userDetails != null}">
|
||||
<form method="post" action="/logout">
|
||||
<input type="image" src="content/app/img/key.png" width="75" alt="submit" class="icon"
|
||||
onclick="return confirm()"/>
|
||||
</form>
|
||||
</c:if>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
</div>
|
||||
43
resources/WEB-INF/jsp/login.jsp
Normal file
43
resources/WEB-INF/jsp/login.jsp
Normal file
@ -0,0 +1,43 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Charm Login</title>
|
||||
<%@ include file="style.html" %>
|
||||
</head>
|
||||
<body>
|
||||
<%@ include file="header.jsp" %>
|
||||
<div>
|
||||
<form method="post" action="/login">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h3>${wordBundle.getWord("email")}</h3></td>
|
||||
<td><input type="email" name="email" placeholder="user@charm.ru"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><h3>${wordBundle.getWord("password")}</h3></td>
|
||||
<td><input type="password" name="password"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table>
|
||||
<tr class="hiddenRow">
|
||||
<td>
|
||||
<a href="/registration"><img src="content/app/img/pencil.png" width="75"></a>
|
||||
</td>
|
||||
<td>
|
||||
<input type="image" src="content/app/img/arrow-right.png" width="75" alt="submit"
|
||||
class="icon"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
<div style="color: red">
|
||||
<c:forEach var="error" items="${errors}">
|
||||
<span>${wordBundle.getWord(error)}</span>
|
||||
<br>
|
||||
</c:forEach>
|
||||
</div>
|
||||
</div>
|
||||
<%@ include file="footer.jsp" %>
|
||||
</body>
|
||||
</html>
|
||||
@ -21,10 +21,11 @@ public class ContentController extends HttpServlet {
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
try {
|
||||
String contentPath = req.getRequestURI().replace("/content/", "");
|
||||
String contentPath = req.getRequestURI().replace("/content", "");
|
||||
log.info("Content download request: {} {}", req.getRequestURI(), contentPath);
|
||||
resp.setContentType("application/octet-stream");
|
||||
service.download(contentPath, resp.getOutputStream());
|
||||
log.info("Content successfully downloaded: {}", contentPath);
|
||||
log.info("Content successfully downloaded: {}", req);
|
||||
} catch (IOException e) {
|
||||
log.error("Content download failed: {}", e.getMessage());
|
||||
resp.sendError(SC_NOT_FOUND);
|
||||
|
||||
@ -7,27 +7,21 @@ import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.charm.back.controller.filter.ErrorFilter;
|
||||
import ru.charm.back.dto.ProfileGetDto;
|
||||
import ru.charm.back.dto.ProfileUpdateDto;
|
||||
import ru.charm.back.mapper.RequestToProfileUpdateDtoMapper;
|
||||
import ru.charm.back.model.Gender;
|
||||
import ru.charm.back.model.Profile;
|
||||
import ru.charm.back.model.exception.DuplicateEmailException;
|
||||
import ru.charm.back.service.ProfileService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Optional;
|
||||
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
|
||||
@WebServlet("/email")
|
||||
@WebServlet("/credentials")
|
||||
@Slf4j
|
||||
@MultipartConfig
|
||||
public class EmailController extends HttpServlet {
|
||||
public class CredentialsController extends HttpServlet {
|
||||
private final ProfileService service = ProfileService.getInstance();
|
||||
private final RequestToProfileUpdateDtoMapper requestToProfileUpdateDtoMapper = RequestToProfileUpdateDtoMapper.getInstance();
|
||||
|
||||
46
src/ru/charm/back/controller/LoginController.java
Normal file
46
src/ru/charm/back/controller/LoginController.java
Normal file
@ -0,0 +1,46 @@
|
||||
package ru.charm.back.controller;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.annotation.WebServlet;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ru.charm.back.dto.LoginDto;
|
||||
import ru.charm.back.dto.ProfileGetDto;
|
||||
import ru.charm.back.dto.RegistrationDto;
|
||||
import ru.charm.back.mapper.RequestToLoginDtoMapper;
|
||||
import ru.charm.back.mapper.RequestToRegistrationDtoMapper;
|
||||
import ru.charm.back.service.ProfileService;
|
||||
import ru.charm.back.validator.RegistrationValidator;
|
||||
import ru.charm.back.validator.ValidationResult;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
@WebServlet("/login")
|
||||
@Slf4j
|
||||
public class LoginController extends HttpServlet {
|
||||
private final ProfileService service = ProfileService.getInstance();
|
||||
|
||||
private final RequestToLoginDtoMapper requestTologinDtoMapper = RequestToLoginDtoMapper.getInstance();
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
req.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
|
||||
LoginDto dto = requestTologinDtoMapper.map(req);
|
||||
Optional<ProfileGetDto> userDetailOpt = service.login(dto);
|
||||
|
||||
if (userDetailOpt.isPresent()) {
|
||||
req.getSession().setAttribute("userDetails", userDetailOpt.get());
|
||||
|
||||
resp.sendRedirect(String.format("/profile?id=%s", userDetailOpt.get().getId()));
|
||||
return;
|
||||
}
|
||||
resp.sendRedirect("/login");
|
||||
}
|
||||
}
|
||||
24
src/ru/charm/back/controller/LogoutController.java
Normal file
24
src/ru/charm/back/controller/LogoutController.java
Normal file
@ -0,0 +1,24 @@
|
||||
package ru.charm.back.controller;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.annotation.WebServlet;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ru.charm.back.dto.LoginDto;
|
||||
import ru.charm.back.dto.ProfileGetDto;
|
||||
import ru.charm.back.mapper.RequestToLoginDtoMapper;
|
||||
import ru.charm.back.service.ProfileService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
@WebServlet("/logout")
|
||||
public class LogoutController extends HttpServlet {
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
req.getSession().invalidate();
|
||||
resp.sendRedirect("/login");
|
||||
}
|
||||
}
|
||||
43
src/ru/charm/back/controller/filter/AuthFilter.java
Normal file
43
src/ru/charm/back/controller/filter/AuthFilter.java
Normal file
@ -0,0 +1,43 @@
|
||||
package ru.charm.back.controller.filter;
|
||||
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.annotation.WebFilter;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ru.charm.back.dto.ProfileGetDto;
|
||||
import ru.charm.back.model.Role;
|
||||
import ru.charm.back.service.WordBundle;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
@WebFilter("/*")
|
||||
@Slf4j
|
||||
public class AuthFilter implements Filter {
|
||||
private final Set<String> PRIVATE_PATHS = Set.of("/profile", "/credentials");
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse res = (HttpServletResponse) servletResponse;
|
||||
|
||||
if (PRIVATE_PATHS.contains(req.getRequestURI())) {
|
||||
ProfileGetDto userDetails = (ProfileGetDto) req.getSession().getAttribute("userDetails");
|
||||
log.info("User {} is trying to access {}", userDetails, req.getRequestURI());
|
||||
if (userDetails == null) {
|
||||
res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
} else if (userDetails.getRole() == Role.ADMIN || userDetails.getId().toString().equals( req.getParameter("id"))) {
|
||||
filterChain.doFilter(req, res);
|
||||
} else {
|
||||
res.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
} else {
|
||||
filterChain.doFilter(req, res);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package ru.charm.back.dao;
|
||||
|
||||
import ru.charm.back.model.Gender;
|
||||
import ru.charm.back.model.Profile;
|
||||
import ru.charm.back.model.Role;
|
||||
import ru.charm.back.model.Status;
|
||||
|
||||
import java.time.LocalDate;
|
||||
@ -32,6 +33,7 @@ public class ProfileDao {
|
||||
profile.setAbout("I am QA");
|
||||
profile.setGender(Gender.MALE);
|
||||
profile.setStatus(Status.ACTIVE);
|
||||
profile.setRole(Role.ADMIN);
|
||||
this.storage.put(1L, profile);
|
||||
Profile profile1 = new Profile();
|
||||
profile1.setId(2L);
|
||||
@ -43,6 +45,7 @@ public class ProfileDao {
|
||||
profile1.setAbout("I am Java Dev");
|
||||
profile1.setGender(Gender.FEMALE);
|
||||
profile1.setStatus(Status.INACTIVE);
|
||||
profile1.setRole(Role.USER);
|
||||
this.storage.put(2L, profile1);
|
||||
this.idStorage = new AtomicLong(3L);
|
||||
}
|
||||
@ -80,4 +83,10 @@ public class ProfileDao {
|
||||
public Set<String> getAllEmails() {
|
||||
return storage.values().stream().map(Profile::getEmail).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Optional<Profile> findByEmail(String email) {
|
||||
if (email == null) return Optional.empty();
|
||||
|
||||
return storage.values().stream().filter(profile -> profile.getEmail().equals(email)).findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
9
src/ru/charm/back/dto/LoginDto.java
Normal file
9
src/ru/charm/back/dto/LoginDto.java
Normal file
@ -0,0 +1,9 @@
|
||||
package ru.charm.back.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LoginDto {
|
||||
private String email;
|
||||
private String password;
|
||||
}
|
||||
@ -1,21 +1,26 @@
|
||||
package ru.charm.back.dto;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import ru.charm.back.model.Gender;
|
||||
import ru.charm.back.model.Role;
|
||||
import ru.charm.back.model.Status;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class ProfileGetDto {
|
||||
private Long id;
|
||||
private String email;
|
||||
private String name;
|
||||
private String surname;
|
||||
private LocalDate birthDate;
|
||||
private Integer age;
|
||||
private String about;
|
||||
private Gender gender;
|
||||
private Status status;
|
||||
private String photo;
|
||||
Long id;
|
||||
String email;
|
||||
String name;
|
||||
String surname;
|
||||
LocalDate birthDate;
|
||||
Integer age;
|
||||
String about;
|
||||
Gender gender;
|
||||
Status status;
|
||||
String photo;
|
||||
Role role;
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ public class ProfileToProfileGetDtoMapper implements Mapper<Profile, ProfileGetD
|
||||
dto.setGender(profile.getGender());
|
||||
dto.setStatus(profile.getStatus());
|
||||
dto.setPhoto(profile.getPhoto());
|
||||
dto.setRole(profile.getRole());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package ru.charm.back.mapper;
|
||||
|
||||
import ru.charm.back.dto.RegistrationDto;
|
||||
import ru.charm.back.model.Profile;
|
||||
import ru.charm.back.model.Role;
|
||||
import ru.charm.back.model.Status;
|
||||
|
||||
public class RegistrationDtoToProfileMapper implements Mapper<RegistrationDto, Profile> {
|
||||
@ -25,6 +26,7 @@ public class RegistrationDtoToProfileMapper implements Mapper<RegistrationDto, P
|
||||
profile.setEmail(dto.getEmail());
|
||||
profile.setPassword(dto.getPassword());
|
||||
profile.setStatus(Status.INACTIVE);
|
||||
profile.setRole(Role.USER);
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
|
||||
29
src/ru/charm/back/mapper/RequestToLoginDtoMapper.java
Normal file
29
src/ru/charm/back/mapper/RequestToLoginDtoMapper.java
Normal file
@ -0,0 +1,29 @@
|
||||
package ru.charm.back.mapper;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import ru.charm.back.dto.LoginDto;
|
||||
import ru.charm.back.dto.RegistrationDto;
|
||||
|
||||
public class RequestToLoginDtoMapper implements Mapper<HttpServletRequest, LoginDto> {
|
||||
|
||||
private static final RequestToLoginDtoMapper INSTANCE = new RequestToLoginDtoMapper();
|
||||
|
||||
private RequestToLoginDtoMapper() {
|
||||
}
|
||||
|
||||
public static RequestToLoginDtoMapper getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginDto map(HttpServletRequest req) {
|
||||
return map(req, new LoginDto());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginDto map(HttpServletRequest req, LoginDto dto) {
|
||||
dto.setEmail(req.getParameter("email"));
|
||||
dto.setPassword(req.getParameter("password"));
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@ -17,4 +17,5 @@ public class Profile {
|
||||
private Gender gender;
|
||||
private Status status;
|
||||
private String photo;
|
||||
private Role role;
|
||||
}
|
||||
6
src/ru/charm/back/model/Role.java
Normal file
6
src/ru/charm/back/model/Role.java
Normal file
@ -0,0 +1,6 @@
|
||||
package ru.charm.back.model;
|
||||
|
||||
public enum Role {
|
||||
ADMIN,
|
||||
USER
|
||||
}
|
||||
@ -4,6 +4,7 @@ import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import ru.charm.back.dao.ProfileDao;
|
||||
import ru.charm.back.dto.LoginDto;
|
||||
import ru.charm.back.dto.ProfileGetDto;
|
||||
import ru.charm.back.dto.ProfileUpdateDto;
|
||||
import ru.charm.back.dto.RegistrationDto;
|
||||
@ -66,4 +67,10 @@ public class ProfileService {
|
||||
public boolean delete(Long id) {
|
||||
return dao.delete(id);
|
||||
}
|
||||
|
||||
public Optional<ProfileGetDto> login(LoginDto dto) {
|
||||
return dao.findByEmail(dto.getEmail())
|
||||
.filter(profile -> profile.getPassword().equals(dto.getPassword()))
|
||||
.map(profileToProfileGetDtoMapper::map);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user