Tartalomjegyzék

< Spring boot

Azonosítás

Kezdő projekt

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

Token kezelése

Szükségünk van egy modellre User.java néven:

src/main/java/lan/zold/emp/User.java
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:

src/main/java/lan/zold/emp/UserRespository.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.

src/main/java/lan/zold/emp/AuthController.java
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.

Employee post védelme

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;
	}	

Felhasználók kezelés

Fel kell tudnunk venni felhasználót:

UserController.java
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'

Jelszavak titkosítása

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:

src/main/java/lan/zold/emp/EmpApplication.java
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.

src/main/java/lan/zold/emp/PasswordService.java
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:

src/main/java/lan/zold/emp/UserController.java
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;
  }
 
}

Titkosított jelszó ellenőrzése

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";
    }

HTTPie teszt

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.

GitHub