emp/ |-.mvn/ |-.vscode |-src/ | |-main/ | | |-java/lan/zold/emp/ | | | |-EmpApplication.java | | | |-Employee.java | | | |-EmployeeControler.java | | | |-EmployeeRepository.java | | | |-User.java | | | |-UserController.java | | | `-UserRepository.java | | `-resources/ | | |-static/ | | |-templates/ | | `-application.properties | `-test/java/lan/zold/emp/ | `-EmpApplicationTests.java |-target/ |-.gitignore |-HELP.me |-mvnw |-mvnw.cmd `-posm.xml
Szükségünk van egy modellre User.java néven:
package lan.zold.emp; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
Itt tároljuk a felhasználó azonosítóját, nevét és jelszavát.
Szükség van egy kapcsolatra az adatbázis tárolóval. Ez lesz a UserRepository.java:
package lan.zold.emp; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Integer> {}
Jpa tárolót használunk.
Végül a három metódust készítünk:
A login() metódust intézi a beléptetést. A sikeres belépés után generálunk egy tokent. Ezt a getToken() metódus végzi.
A checkToken() metódust más kontrollerekből hívjuk, olyan útvonalaknál amit szeretnénk védeni.
package lan.zold.emp; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; @RestController @RequestMapping("/api") public class AuthController { @Autowired UserRepository userRepository; @CrossOrigin @PostMapping("/login") public @ResponseBody String login(@RequestBody User user) { //Ide jön felhasználó azonosítás String token = getToken(user.getName()); return token; } public String getToken(String name) { String key = "adsfffd"; Map<String, Object> claims = new HashMap<>(); String token = Jwts.builder() .setSubject(name) .setClaims(claims) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() * 500_000)) .signWith(SignatureAlgorithm.HS512, key.getBytes()) .compact(); return token; } public String checkToken(String token) { try { // A titkos kulcs, amivel a token alá lett írva String key = "adsfffd"; // Token ellenőrzése és annak tartalmának kiolvasása Claims claims = Jwts.parser().setSigningKey(key.getBytes()).parseClaimsJws(token).getBody(); // Token ellenőrzése, hogy lejárt-e Date expirationDate = claims.getExpiration(); Date currentDate = new Date(); if (expirationDate.before(currentDate)) { return "Lejárt token"; } // Token érvényes return "Érvényes token"; } catch (SignatureException e) { // Hibás aláírás esetén return "Hibás token"; } catch (Exception e) { // Egyéb hibák esetén return "Hiba történt: " + e.getMessage(); } } }
A felhasználó azonosítás itt még nincs kész, így tokent mindig megkapjuk.
A védelem azonban már működik. Az employees végpontot POST metódus esetén így csak az érvényes token elküldésével érhetjük el:
@CrossOrigin @PostMapping(path="/employees") public Employee store( @RequestBody Employee emp, @RequestHeader("Authorization") String tokenHeader) { Employee res = null; String token = tokenHeader.replace("Bearer ", ""); AuthController authController = new AuthController(); try { String tokenOk = authController.checkToken(token); if(tokenOk.equals("tokenok")) { res = empRepository.save(emp); }else { String msg = "Hiba! A token nem megfelelő!"; throw new IllegalArgumentException(msg); } } catch (Exception e) { System.err.println("Hiba! A token nem jó!"); } return res; }
Fel kell tudnunk venni felhasználót:
package lan.zold.emp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class UserController { @Autowired UserRepository userRepository; @CrossOrigin @PostMapping("/registry") public User store(@RequestBody User user) { User res = userRepository.save(user); return res; } }
Most alakítsuk át az AuthController.java fájlban a login() metódust:
@CrossOrigin @PostMapping("/login") public @ResponseBody String login(@RequestBody User user) { var users = userRepository.findAll(); var storedUser = users.stream() .filter( userd -> userd.getName() .equalsIgnoreCase( userd.getName() ) ) .findFirst() .get(); String reqName = user.getName(); String reqPassword = user.getPassword(); if(storedUser.getName().equals(reqName) && storedUser.getPassword().equals(reqPassword)) { String token = getToken(user.getName()); return token; } return "Hiba! Sikeretlen"; }
Vegyünk fel egy felhasználót:
http post http://localhost:8080/api/registry name='valaki' password='titok'
Lépjünk be:
http post http://localhost:8080/api/login name='valaki' password='titok'
A jelszavak titkosításához a Sping Security titkosítóját használjuk, de nem szeretnék a teljes komponensre beállítani a Sprint Security-t. Ezért az EmpApplication.java fájlban, a @SpringBootApplication annotációt egészítsük ki:
package lan.zold.emp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; @SpringBootApplication(exclude = { SecurityAutoConfiguration .class }) public class EmpApplication { public static void main(String[] args) { SpringApplication.run(EmpApplication.class, args); } }
Most már felvehetjük a Sprint Security-t függőségnek:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
Ez után vegyünk fel egy új szolgáltatást PasswordService néven.
package lan.zold.emp; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; @Service public class PasswordService { public String enPass(String clearPass) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String enPass = encoder.encode(clearPass); return enPass; } }
Most már használhatjuk a UserController.java fájlban:
package lan.zold.emp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class UserController { @Autowired UserRepository userRepository; @Autowired PasswordService passwordService; @CrossOrigin @PostMapping("/registry") @ResponseStatus(code = HttpStatus.CREATED) public User store(@RequestBody User user) { String enpass = passwordService.enPass(user.getPassword()); user.setPassword(enpass); User res = userRepository.save(user); return res; } }
A PasswordService.java fájlban vegyünk fel egy checkPass() metódust.
public boolean checkPass(String clearPass, String enPass) { return encoder.matches(clearPass, enPass); }
Az AuthController.java fájlban is titkosított jelszót kell ellenőrizünk.
@CrossOrigin @PostMapping("/login") public @ResponseBody String login(@RequestBody User user) { var users = userRepository.findAll(); var storedUser = users.stream() .filter( userd -> userd.getName() .equalsIgnoreCase( userd.getName() ) ) .findFirst() .get(); String reqName = user.getName(); boolean passOk = passwordService.checkPass(user.getPassword(), storedUser.getPassword()); if(storedUser.getName().equals(reqName) && passOk) { String token = getToken(user.getName()); return token; } return "Hiba! Sikeretlen"; }
Vegyünk fel egy felhasználót:
http post http://localhost:8080/api/registry name='mari' password='titok'
Lépjünk be a felhasználóval:
http post http://localhost:8080/api/login name='mari' password='titok'
Védett útvonalak tesztje:
http post http://localhost:8080/api/employees name='mari' city='Pécs' salary=398 -A bearer -a eyJhbGc...
A -a és a -A kapcsolók vagy a legvégén vagy http parancs után közvetlenül jönnek.