Sandra Lewandowska

Witaj w mojej przestrzeni!

Angular 6 + Spring Boot + reCAPTCHA w przesyłanym formularzu

Po pierwsze należy wygenerować indywidulany dla domeny (może być też localhost dla testów lokalnych) sitekey i secret –> Google reCAPTCHA

Integracja po stronie klienta
W formularzu dodajemy kod reCAPTCHA z wygenerowanym sitekey

<div class="form-group">
<div class="g-recaptcha" data-sitekey="6Lf7anUUAAAAAHobMeDJkbR_xxxasasasa"></div>
</div>

Następnie w komponencie z formularzem w metodzie ngOnInit() dodajemy do strony skrypt z api reCAPTCHA. Jeśli skrypt zostałby dodany do index.html, to reCAPTCHA pojawiałaby się tylko po odświeżeniu strony.

ngOnInit(){ this.addScript(); }
	addScript() {
    let script = document.createElement('script');
    script.src = 'https://www.google.com/recaptcha/api.js';
    script.async = true;
    script.defer = true;
    document.body.appendChild(script);
}

Przed wysłaniem formularza należy sprawdzić czy pole “Nie jestem robotem” zostało zaznaczone.

onSubmit() {
    this.submitted = true;
	 const response = grecaptcha.getResponse();
         if (response.length === 0) {
            alert('Recaptcha not verified.');
            return;
        }
    this.save(); 
  }
	
	save() { 
    this.accountService.createNewUser(this.registerForm.value, grecaptcha.getResponse())
    .subscribe(
        data => { console.log(data); },
        error => { console.log(error); })
 }

Integracja po stronie serwera – tutaj przyda się przydzielony wcześniej kod secret

@PostMapping("/new")
	    @ResponseStatus(HttpStatus.CREATED)
		public void  createUser(@RequestBody UserDTO user, @RequestParam(name="response") String recaptchaResponse) {
	    	captchaVerification.verify(recaptchaResponse);
			userRepository.findByEmailIgnoreCase(user.getEmail())
			.ifPresent(u -> {throw new ResourceExistsException("User with login: "+user.getEmail()+" exist");});
	   	  	User createdUser = userService.createUser(user);
		}

Przed dodaniem nowego użytkownika do bazy danych należy sprawdzić poprawność recaptchaResponse, wysyłając zapytanie przez recaptcha api.

import java.net.URI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service("captchaVerification")
public class CaptchaVerification {

	 	@Autowired
	    private CaptchaSettings captchaSettings;

	 	public void verify(String response) {
       
        URI verifyUri = URI.create(String.format(
          "https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s",
          captchaSettings.getSecret(), response));
        
        RestTemplate restTemplate = new RestTemplate();
        GoogleResponse googleResponse = restTemplate.getForObject(verifyUri, GoogleResponse.class);
 
        if(!googleResponse.isSuccess()) {
            throw new InvalidRecaptchaException();
        }
    }
}

Sitekey i Secret umieszczone są w application.properties

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "google.recaptcha.key")
public class CaptchaSettings {
 
    private String site;
    private String secret;
    
	public String getSite() {
		return site;
	}
	public void setSite(String site) {
		this.site = site;
	}
	public String getSecret() {
		return secret;
	}
	public void setSecret(String secret) {
		this.secret = secret;
	}

}

Klasa GoogleResponse jest schematem reprezentującym odpowiedź przesłaną od Google Api i pomaga stwierdzić poprawność walidacji reCAPRTCHA.

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({
    "success",
    "challenge_ts",
    "hostname",
    "error-codes"
})
public class GoogleResponse {

	 @JsonProperty("success")
	    private boolean success;
	     
	    @JsonProperty("challenge_ts")
	    private String challengeTs;
	     
	    @JsonProperty("hostname")
	    private String hostname;
	     
	    @JsonProperty("error-codes")
	    private ErrorCode[] errorCodes;
	 
	    @JsonIgnore
	    public boolean hasClientError() {
	        ErrorCode[] errors = getErrorCodes();
	        if(errors == null) {
	            return false;
	        }
	        for(ErrorCode error : errors) {
	            switch(error) {
	                case InvalidResponse:
	                case MissingResponse:
	                    return true;
	            }
	        }
	        return false;
	    }
	 
	    static enum ErrorCode {
	        MissingSecret,     InvalidSecret,
	        MissingResponse,   InvalidResponse;
	 
	        private static Map<String, ErrorCode> errorsMap = new HashMap<String, ErrorCode>(4);
	 
	        static {
	            errorsMap.put("missing-input-secret",   MissingSecret);
	            errorsMap.put("invalid-input-secret",   InvalidSecret);
	            errorsMap.put("missing-input-response", MissingResponse);
	            errorsMap.put("invalid-input-response", InvalidResponse);
	        }
	 
	        @JsonCreator
	        public static ErrorCode forValue(String value) {
	            return errorsMap.get(value.toLowerCase());
	        }
	    }

		public boolean isSuccess() {
			return success;
		}

		public void setSuccess(boolean success) {
			this.success = success;
		}

		public String getChallengeTs() {
			return challengeTs;
		}

		public void setChallengeTs(String challengeTs) {
			this.challengeTs = challengeTs;
		}

		public String getHostname() {
			return hostname;
		}

		public void setHostname(String hostname) {
			this.hostname = hostname;
		}

		public ErrorCode[] getErrorCodes() {
			return errorCodes;
		}

		public void setErrorCodes(ErrorCode[] errorCodes) {
			this.errorCodes = errorCodes;
		}
	     
}

Powyższe fragmenty kodu są też zawarte w projekcie Clinic (clinic-server, clinic-client) na GitHub >>tutaj<<

Clinic – klient Angular + serwer Java + PostgreSQL

O aplikacji

Aplikacja Clinic to właściwie zalążek pomysłu na wirtualną przychodnię. Projekt zawiera w sobie elementy, które były dodane z myślą o dalszej rozbudowie. Składa się z klienta w Angularze 6 i części serwerowej opartej na Spring Boot 2 i Javie 8. Clinic to hobbystyczne przedsięwzięcie mające na celu zbudowanie aplikacji wykorzystującej nowe technologie.
Do tej pory zrealizowane m.in. zostały:
– przykładowa komunikacja REST między klientem a serwerem za pomocą obiektów JSON,
– autoryzacja użytkownika za pomocą Spring Security i OAuth2,
– połączenie z bazą danych PosgreSQL,
– walidacja pól w formularzu rejestracji,
– Google reCaptcha w formularzu rejestracji.

Na stronie głównej umieszczony jest przykad pobierania danych. Z rozwijanej listy użytkownik może wybrać specjalizację lekarza, w liście po prawej pojawią się nazwiska lekarzy przypinych do wybranej specjalizacji. Wyszukiwarka miała ułatwiać wyszukanie konkretnych wizyt. Wizyty nie zostały jeszcze obsłużone.

Dostępny dla wszystkich jest panel logowania. Przykładowe dane to
user: user@user
pass: useruser

Logowanie użytkownika, zapytanie do serwera.

login(user: UserDTO) {
      this.headers = new Headers({
          "Content-Type": "application/x-www-form-urlencoded",
          "Authorization": "Basic " + btoa(environment.clientId+':'+environment.clientSecret)
        });
      this.options = new RequestOptions({ headers: this.headers });
      this.cred = "username=" + user.email + "&password=" + user.password + "&grant_type=password";
      return this.http.post(environment.authUrl, this.cred, this.options)
      .pipe(map(res => {
      localStorage.setItem('currentUser', JSON.stringify({userName:user.email, token: res.json().access_token}));
      this.router.navigateByUrl("/panel");
      return res;
      }));
  }

Po zalogowaniu użytkownik może przejść do panelu użytkownika. Pobierane są tam imię i nazwisko na podstawie przesłanego tokena.
Zapytanie przesłane do serwera:

updateUserInfo(): Observable<any>{
      this.headers = new Headers({
          "Content-Type": "application/json",
          "Authorization": "Bearer " + this.getToken()
      });
      this.options = new RequestOptions({ headers: this.headers });
      return this.http.get(`${environment.baseApiUrl}` + `/api/users/me`, this.options)
        .pipe(map(res => res.json()));
}

Obsługa URI w kontrolerze:

@GetMapping("/me")
	  public User currentUser() {
	      Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
	      return userRepository.findByEmailIgnoreCase(authentication.getName()).orElse(null);
	 }

Rejestracja – dostępna dla wszystkich, ale został dodany kod reCaptcha, aby wyeliminować działanie botów.

Kod – serwer https://github.com/sanlew/clinic-server

Kod – klient https://github.com/sanlew/clinic-client

Prezentacja online https://sanlew.github.io/clinic-client
Czasami komunikacja z serwerem wolno działa. Część serwerowa znajduje się na Heroku – https://clinic-serv.herokuapp.com/
Część kliencka została umieszczona na GitHub Pages a serwerowa na Heroku.

Uruchomienie aplikacji

1.Serwer: $ mvn spring-boot:run
2.Klient:
$ npm install
$ ng serve

Odmiana nazwisk

O aplikacji

Aplikacja Lastnames pomaga odmieniać polskie nazwiska przez przypadki. Użytkownik wpisuje wybrane nazwisko w mianowniku rodzaju męskiego, następnie zaznacza czy chce odmienić formę dla kobiety, mężczyzny lub liczby mnogiej. Po wciśnięciu “OK” program przeszukuje plik tekstowy, aby dopasować podane nazwisko do wzorca odmiany. Uwaga. Program posiada ograniczoną bazę wzorców i mimo dołożonych starań, mogą pojawić się nazwiska, których odmiana nie będzie uwzględniona lub będzie niepoprawna. Niektóre nazwiska posiadają również więcej niż jedną dopuszczalną formę odmiany, np. Gołąb (dopełniacz Gołębia lub Gołąba)

W programie użyto:

JavaFX 8 – odpowiada za graficzny interfejs aplikacji z pomocą Gluon Scene Builder,
Java 8 – jako minimalny wymóg do współpracy z JavaFX 8,
JUnit 4 – proste testy do sprawdzenia poprawnej odmiany,
regexp – wyrażenia regularne do odnalezienia odpowiedniej reguły odmiany nazwiska.

Kod – https://github.com/sanlew/lastnames

Uruchomienie aplikacji

1. mvn clean install -U
2. mvn jfx:jar
3. cd target/jfx/app
4. java -jar lastnames-0.0.1-SNAPSHOT-jfx.jar

St3g0 v.0.3

O aplikacji

Aplikacja St3g0 v.0.3 służy do steganografii plików graficznych i jest częścią programu pisanego w ramach studiów.

Więcej szczegółów na temat algorytmu LSB we wpisie: https://sandralewandowska.pl/steganografia/383/lsb/

Po wybraniu przycisku Next zostaje wywołana metoda validate() danego kontrolera. Jeśli brakuje pola lub jest uzupełnione nieprawidłowo, wyświetlony zostaje komunikat o błędzie a przejście do kolejnego kroku jest blokowane.

Użyte technologie/narzędzia/biblioteki:

JavaFX 8 – odpowiada za graficzny interfejs aplikacji z pomocą Gluon Scene Builder,
Java 8 – jako minimalny wymóg do współpracy z JavaFX 8,
Maven – do pobrania bibliotek wymaganych w projekcie,
Google Guice – użycie adnotacji @Inject dla pól pozwala otrzymać udostępniony obiekt WindowData. Ułatwia to przedstawienie poszczególnych kroków formularza na osobnych widokach,
slf4j z log4j – wyświetlanie/zapis logów.

Prezentację aplikacji można zobaczyć na poniższych filmach.
W pierwszym wideo ukrywaną informacją jest plik graficzny, a w drugim zwykły tekst.

Link do kodu: Kod – https://github.com/sanlew/st3g0

Uruchomienie aplikacji

1. mvn clean install -U
2. mvn jfx:jar
3. cd target/jfx/app
4. java -jar stego-0.0.1-SNAPSHOT-jfx.jar