Spring Security Intro
Spring Security Intro
Daftar ISI..............................................................................................................2
1. Spring Boot Security......................................................................................6
2. Apa perbedaan utama antara otentikasi JWT dan OAuth?.............................8
3. JWT (JSON Web Token)...............................................................................9
4. Cara kerja JWT.............................................................................................11
a. Spring Security JWT in Spring Boot 2..............................................................11
5. Contoh dan Implementasi.............................................................................14
a. Pom.XML.........................................................................................................14
b. Entitas...............................................................................................................15
c. Repository.........................................................................................................19
d. Service..............................................................................................................20
e. Configurasi Security.........................................................................................24
f. Run and Testing with Postman..........................................................................26
g. Gitlab Project....................................................................................................28
6. Register.........................................................................................................28
a. Register Manual................................................................................................28
b. Register By OTP...............................................................................................31
c. Konfirmasi OTP Token User: guna untuk mengupdate user agar dapat login...37
7. Forget Password...........................................................................................38
a. Forget Password OTP Email............................................................................38
b. Test Postman.....................................................................................................43
8. Filter And Sorting.........................................................................................44
9. File Handling................................................................................................47
a. Upload File.......................................................................................................47
b. Service api.........................................................................................................48
c. Revisi Kode Program di class FileController....................................................54
d. Tambahkan pada class main..............................................................................55
e. Testing..............................................................................................................56
Upload 1 file.....................................................................................................................56
2
Upload many file..............................................................................................................57
Download file...................................................................................................................58
f. Download file tampa download / show file only...............................................58
10. Deploy Project ke Heroku.........................................................................60
a. Buat akun Heroku.............................................................................................60
b. Buat Akun Github.............................................................................................60
c. Masuk Ke akun Heroku, untuk deploy aplikasi.................................................60
d. Membuat database di heroku.............................................................................64
e. Membuat env dan staging di spring boot...........................................................66
f. Tambahkan pada pom.xml................................................................................67
g. Aotomatic deploy heroku..................................................................................67
11. Register OTP by URI Tymeleaf...................................................................70
a. Step 1-Controller...............................................................................................70
b. Step 2- Apllication.properties...........................................................................71
c. Step 3- Controller Tymeleaf..............................................................................72
d. Step 4- UI Tymeleaf..........................................................................................73
e. Test Run Postman.............................................................................................74
12. CronJob Scheduler....................................................................................75
a. Step 1- Application Properties..........................................................................75
b. Config...............................................................................................................76
c. Main Application..............................................................................................77
d. Cronjob Example..............................................................................................78
13. Download image dari gambar kemudian simpan ke local........................78
14. Docker spring boot....................................................................................78
a. Step 1................................................................................................................79
b. Step 2 : .env......................................................................................................79
c. Step 3 : docker-compose.yml............................................................................79
d. Step 4: run and noted........................................................................................82
a. docker-compose up -d : menajalankan semua yang semuar servise.................82
b. docker-compose up spring : jalankan image spring saja...................................82
c. docker-compose images / docker-compose ps : show all image yg aktif.........82
d. docker-compose down -v : mematikan semua service yang ada.......................82
3
e. docker-compose ps : melihart image yang sedang aktif....................................82
f. remote images : https://stackoverflow.com/questions/37971961/docker-error-
bind-address-already-in-use pass: 12345................................................................82
g. docker-compose logs : melihat logs..................................................................82
h. docker-compose logs -f postgres : lebih detail , melihat logs khusu container
service spring...........................................................................................................82
i. jalankan postgresdi docker secara manual https://towardsdatascience.com/local-
development-set-up-of-postgresql-with-docker-c022632f13ea................................82
j. docker-compose run -d \--name dev-postgres \-e
POSTGRES_PASSWORD=Pass2020! \-v
${HOME}/postgres-data/:/var/lib/postgresql/data \-p 5432:5432 binar...................82
15. Send File To Email....................................................................................83
a. Step 1 – strukture..............................................................................................83
b. Step 2 :code.......................................................................................................83
c. Step 3 : pom.xml and application.properties.....................................................86
d. Step 4: resouces.................................................................................................86
e. Step 5 : Testing By Junit...................................................................................88
16. Login with username and password only..................................................89
a. Step 1- pom.xml................................................................................................89
b. Step 2- buatlah class userservice.......................................................................90
c. Step 3- user impl...............................................................................................90
d. Model – request.................................................................................................92
e. Config-allow uri................................................................................................93
f. Controller..........................................................................................................93
g. Testing..............................................................................................................94
17. Refresh TOKEN........................................................................................95
a. Step 1- lakukan login........................................................................................95
b. Step 2- to do refresh token................................................................................96
18. Upload dan simpan barang satu enpoind...................................................96
c. Output...............................................................................................................98
19. Validation Password saat register.............................................................98
a. Step 1- Pom.xml................................................................................................98
b. Step 2-application properties.............................................................................99
4
c. Step 3- struktur kode.......................................................................................100
d. Step 3 a – setting Character Password dimana?..............................................106
e. Step 4 – cara menggunakan method validation password ?.............................106
f. Tester..............................................................................................................107
20. Menampilkan eror validation hibernite...................................................108
a. Strukture..........................................................................................................108
b. Controller........................................................................................................110
c. Entity...............................................................................................................110
d. Testing............................................................................................................111
21. Valid email manual.................................................................................111
22. Anotasi hibernite.....................................................................................111
a. @Max @Min :Batasin Request maksimal 5 huruf..........................................111
b. @Size Batasin Request maksimal 5 huruf.......................................................112
Daftar Pustaka...................................................................................................113
5
a. Spring Boot Security
Spring security merupakan fitur dari framework spring. Spring security
memungkinkan developer untuk mengintegrasikan fitur keamanan pada
aplikasi Java Web dengan cara melakukan hijacking pada HTTP request
menggunakan filter yang melakukan pengecekan keamanan.
6
Gambar Proses Spring security
7
oleh UserDetailsService & PasswordEncoder) untuk
memvalidasi object UsernamePasswordAuthenticationToken . Jika
baerhasil, AuthenticationManager akan mengembalikan data dari
object Authentication (termasuk didalamnya granted authorities).
OncePerRequestFilter melakukan sebuah single execution untuk
setiap request pada API. Ini memberikan sebuah method bernama
doFilterInternal() method yang akan mengimplementasikan proses
parsing & validasi JWT, mengambil User details
(menggunakan UserDetailsService), mengecek Authorizaion
(menggunakan UsernamePasswordAuthenticationToken).
AuthenticationEntryPoint untuk menangkap error pada saat
authentication.
Repository terdiri dari UserRepository & RoleRepository untuk
bekerja dengan Database, akan di import dan digunakan di Controller.
Controller menerima & menangani request setelah request difilter
oleh OncePerRequestFilter.
AuthController menangani requests di proses signup dan login.
TestController kita gunakan untuk mengecek dan menguji hak akses.
8
c. JWT (JSON Web Token)
WT populer untuk Otentikasi dan Pertukaran Informasi. Server
mengkodekan data ke dalam JSON Web Token dan mengirimkannya ke
Klien. Klien menyimpan JWT, kemudian setiap Permintaan dari Klien ke
rute atau sumber daya yang dilindungi harus dilampirkan JWT itu
(biasanya di header). Server akan memvalidasi JWT itu dan
mengembalikan Response.
9
Sesi di Server memiliki waktu kedaluwarsa. Setelah waktu itu, Sesi ini
telah kedaluwarsa dan pengguna harus masuk kembali untuk membuat
Sesi lain.
Jika pengguna telah masuk dan Sesi belum kedaluwarsa, Cookie
(termasuk SessionId) selalu berjalan dengan semua Permintaan HTTP ke
Server. Server akan membandingkan ini SessionIddengan yang
disimpan Sessionuntuk mengotentikasi dan mengembalikan Respons
yang sesuai.
Tidak apa-apa. Tetapi mengapa kita membutuhkan Otentikasi Berbasis
Token ?
Jawabannya adalah kami tidak hanya memiliki situs web, ada banyak
platform di sana.
Asumsikan bahwa kita memiliki situs web yang bekerja dengan baik
dengan Session. Suatu hari, kami ingin menerapkan sistem untuk Seluler
(Aplikasi Asli) dan menggunakan Database yang sama dengan aplikasi
Web saat ini. Apa yang harus kita lakukan? Kami tidak dapat
mengautentikasi pengguna yang menggunakan Aplikasi Asli
menggunakan Otentikasi Berbasis Sesi karena jenis ini tidak memiliki
Cookie.
Haruskah kita membangun proyek backend lain yang mendukung
Aplikasi Asli?
Atau haruskah kita menulis modul Otentikasi untuk pengguna Aplikasi
Asli?
Itu sebabnya Otentikasi berbasis Token lahir.
Dengan metode ini, status login pengguna dikodekan ke dalam JSON
Web Token (JWT) oleh Server dan dikirim ke Klien. Saat ini banyak
RESTful API yang menggunakannya. Mari kita pergi ke bagian
berikutnya, kita akan tahu cara kerjanya.
10
d. Cara kerja JWT
11
Lihat diagram di atas, kita dapat dengan mudah mengaitkan
komponen-komponen ini dengan proses Spring Security
Authentication: menerima permintaan HTTP, filter, authenticate,
store Authentication data, generate token, get User details, authorize,
handle exception
At a glance:
– SecurityContextHolder menyediakan akses ke SecurityContext.
– SecurityContext menyimpan Otentikasi dan mungkin meminta
informasi keamanan khusus.
– Authentication mewakili prinsipal yang mencakup
GrantedAuthority yang mencerminkan izin seluruh aplikasi yang
diberikan kepada prinsipal.
– UserDetails berisi informasi yang diperlukan untuk membangun
objek Otentikasi dari DAO atau sumber data keamanan lainnya
– JwtAuthTokenFilter (extends OncePerRequestFilter) pra-proses
permintaan HTTP, dari Token, buat Otentikasi dan isi ke
SecurityContext.
– JwtProvider memvalidasi, mem-parsing token String atau
menghasilkan String token dari UserDetails.
– UsernamePasswordAuthenticationToken mendapatkan nama
pengguna/kata sandi dari Permintaan masuk dan digabungkan
menjadi turunan dari antarmuka Otentikasi.
– AuthenticationManager uses DaoAuthenticationProvider (with help
12
of UserDetailsService & PasswordEncoder) to validate instance
of UsernamePasswordAuthenticationToken, then returns a fully
populated Authentication instance on successful authentication.
– SecurityContext is established by
calling SecurityContextHolder.getContext().setAuthentication(…) wit
h returned authentication object above.
– AuthenticationEntryPoint handles AuthenticationException.
– Access to Restful API is protected by HTTPSecurity and authorized
with Method Security Expressions.
13
f. Contoh dan Implementasi
Gitlab :
https://gitlab.com/binarxgrab_mainbootcamp/backend_java/boilerplate_bej
Branch : 04032022-spring-security
Kita ingin menambahkan security dengan jwt token pada project
sebelumnya,
Berikut langkah-langkah nya.
a. Pom.XML
Silahkan Tambahkan pada pom.xml:
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<oauth2.version>2.0.14.RELEASE</oauth2.version>
<jwt.version>1.0.9.RELEASE</jwt.version>
</properties>
….
<!-- JWT-->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${oauth2.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
14
b. Entitas
Silahkan tambahkan class dibawah ini :
package com.binar.demo.model.oauth;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import
org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "oauth_user")
public class User implements UserDetails, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
15
@Column(length = 100,unique=true)
private String username;
@Column(length = 100,unique=true)
private String username1;
@JsonIgnore
private String password;
@JsonIgnore
private String verifyToken;
@JsonIgnore
private Date expiredVerifyToken;
@JsonIgnore
private boolean enabled = true;
@JsonIgnore
@Column(name = "not_expired")
private boolean accountNonExpired = true;
@JsonIgnore
@Column(name = "not_locked")
private boolean accountNonLocked = true;
@JsonIgnore
@Column(name = "credential_not_expired")
private boolean credentialsNonExpired = true;
16
public List<Role> getRoles() {
return roles;
}
@Override
public Collection<? extends GrantedAuthority>
getAuthorities() {
return this.roles;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
17
credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
18
this.otpExpiredDate = otpExpiredDate;
}
}
c. Repository
Silahkan tambahkan class repositori seperti gambar dibawah ini :
import com.binar.demo.model.oauth.User;
import org.springframework.data.jpa.repository.Query;
import
org.springframework.data.repository.PagingAndSortingRepository;
19
d. Service
Tambahkan class service seperti pada gambar dibawah ini :
package com.binar.demo.view.oauth;
import com.binar.demo.model.oauth.Client;
import com.binar.demo.model.oauth.Role;
import com.binar.demo.model.oauth.RolePath;
import com.binar.demo.model.oauth.User;
import com.binar.demo.repository.oauth.ClientRepository;
import com.binar.demo.repository.oauth.RolePathRepository;
import com.binar.demo.repository.oauth.RoleRepository;
import com.binar.demo.repository.oauth.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import
org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
@Component
@Service
public class DatabaseSeeder implements ApplicationRunner {
20
private Logger logger =
LoggerFactory.getLogger(DatabaseSeeder.class);
@Autowired
private PasswordEncoder encoder;
@Autowired
private RoleRepository roleRepository;
@Autowired
private ClientRepository clientRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private RolePathRepository rolePathRepository;
@Override
@Transactional
public void run(ApplicationArguments applicationArguments) {
String password = encoder.encode(defaultPassword);
this.insertRoles();
this.insertClients(password);
this.insertUser(password);
}
@Transactional
public void insertRoles() {
for (String role: roles) {
String[] str = role.split(":");
String name = str[0];
21
String type = str[1];
String pattern = str[2];
String[] methods = str[3].split("\\|");
Role oldRole = roleRepository.findOneByName(name);
if (null == oldRole) {
oldRole = new Role();
oldRole.setName(name);
oldRole.setType(type);
oldRole.setRolePaths(new ArrayList<>());
for (String m: methods) {
String rolePathName = name.toLowerCase()
+"_"+m.toLowerCase();
RolePath rolePath =
rolePathRepository.findOneByName(rolePathName);
if (null == rolePath) {
rolePath = new RolePath();
rolePath.setName(rolePathName);
rolePath.setMethod(m.toUpperCase());
rolePath.setPattern(pattern);
rolePath.setRole(oldRole);
rolePathRepository.save(rolePath);
oldRole.getRolePaths().add(rolePath);
}
}
}
roleRepository.save(oldRole);
}
}
@Transactional
public void insertClients(String password) {
for (String c: clients) {
String[] s = c.split(":");
String clientName = s[0];
String[] clientRoles = s[1].split("\\s");
Client oldClient =
clientRepository.findOneByClientId(clientName);
if (null == oldClient) {
oldClient = new Client();
oldClient.setClientId(clientName);
oldClient.setAccessTokenValiditySeconds(28800);//1 jam
3600 :token valid : seharian kerja : normal 1 jam
oldClient.setRefreshTokenValiditySeconds(7257600);// refresh
oldClient.setGrantTypes("password refresh_token
authorization_code");
oldClient.setClientSecret(password);
oldClient.setApproved(true);
oldClient.setRedirectUris("");
oldClient.setScopes("read write");
List<Role> rls =
roleRepository.findByNameIn(clientRoles);
if (rls.size() > 0) {
oldClient.getAuthorities().addAll(rls);
}
}
22
clientRepository.save(oldClient);
}
}
@Transactional
public void insertUser(String password) {
for (String userNames: users) {
String[] str = userNames.split(":");
String username = str[0];
String[] roleNames = str[1].split("\\s");
User oldUser =
userRepository.findOneByUsername(username);
if (null == oldUser) {
oldUser = new User();
oldUser.setUsername(username);
oldUser.setPassword(password);
List<Role> r =
roleRepository.findByNameIn(roleNames);
oldUser.setRoles(r);
}
userRepository.save(oldUser);
}
}
}
23
e. Configurasi Security
Tambahkan configurasi security seperti pada gambar dibawah ini :
import com.binar.demo.view.oauth.Oauth2UserDetailsService;
import com.binar.demo.view.oauth.Oauth2ClientDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.authentication.AuthenticationManager
;
import
org.springframework.security.crypto.password.PasswordEncoder;
import
org.springframework.security.oauth2.config.annotation.configurers
.ClientDetailsServiceConfigurer;
import
org.springframework.security.oauth2.config.annotation.web.configu
ration.AuthorizationServerConfigurerAdapter;
import
org.springframework.security.oauth2.config.annotation.web.configu
ration.EnableAuthorizationServer;
import
org.springframework.security.oauth2.config.annotation.web.configu
rers.AuthorizationServerEndpointsConfigurer;
import
org.springframework.security.oauth2.config.annotation.web.configu
rers.AuthorizationServerSecurityConfigurer;
import
org.springframework.security.oauth2.provider.token.AccessTokenCon
verter;
import
org.springframework.security.oauth2.provider.token.TokenStore;
@Configuration
24
@EnableAuthorizationServer
public class Oauth2AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
@Autowired
private Oauth2ClientDetailsService clientDetailsService;
@Autowired
private Oauth2UserDetailsService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AccessTokenConverter accessTokenConverter;
@Autowired
private TokenStore tokenStore;
/**
* Change server config, password encoder etc.
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer
server) throws Exception {
server.allowFormAuthenticationForClients()
// .tokenKeyAccess("permitAll()")
// .checkTokenAccess("isAuthenticated()")
.passwordEncoder(passwordEncoder)
;
}
/**
* Change client details etc.
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.withClientDetails(clientDetailsService);
}
/**
* Change user details etc.
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer
endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore)
.accessTokenConverter(accessTokenConverter)
.userDetailsService(userDetailsService)
;
}
}
25
f. Run and Testing with Postman
26
27
g. Gitlab Project
Silahkan pull dan download project pada url berikut : Gitlab :
https://gitlab.com/binarxgrab_mainbootcamp/backend_java/boilerplate_bej
Branch : 04032022-spring-security
h. Register
a. Register Manual
https://gitlab.com/binarxgrab_mainbootcamp/backend_java/boilerplate_bej
Branch : 04032022-register
Service
import java.util.Map;
Service Impl
package com.binar.grab.service.impl;
import com.binar.grab.config.Config;
import com.binar.grab.dao.request.RegisterModel;
import com.binar.grab.model.oauth.Role;
import com.binar.grab.model.oauth.User;
import com.binar.grab.repository.oauth.RoleRepository;
import com.binar.grab.repository.oauth.UserRepository;
import com.binar.grab.service.UserService;
import com.binar.grab.util.TemplateResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Service
public class UserServiceImpl implements UserService {
28
LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
RoleRepository repoRole;
@Autowired
UserRepository repoUser;
@Autowired
private PasswordEncoder encoder;
@Autowired
public TemplateResponse templateResponse;
@Override
public Map registerManual(RegisterModel objModel) {
Map map = new HashMap();
try {
String[] roleNames = {"ROLE_USER", "ROLE_READ",
"ROLE_WRITE"}; // admin
User user = new User();
user.setUsername(objModel.getEmail().toLowerCase());
user.setFullname(objModel.getFullname());
//step 1 :
// user.setEnabled(false); // matikan user
String password =
encoder.encode(objModel.getPassword().replaceAll("\\s+", ""));
List<Role> r = repoRole.findByNameIn(roleNames);
user.setRoles(r);
user.setPassword(password);
User obj = repoUser.save(user);
return templateResponse.templateSukses(obj);
} catch (Exception e) {
logger.error("Eror registerManual=", e);
return templateResponse.templateEror("eror:"+e);
}
}
}
Controller
package com.binar.grab.controller;
import com.binar.grab.config.Config;
import com.binar.grab.dao.request.RegisterModel;
import com.binar.grab.model.oauth.User;
import com.binar.grab.repository.oauth.UserRepository;
import com.binar.grab.service.UserService;
import com.binar.grab.util.TemplateResponse;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
29
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user-register/")
public class Register {
@Autowired
private UserRepository userRepository;
@Autowired
public UserService serviceReq;
@Autowired
public TemplateResponse templateCRUD;
@PostMapping("/register")
public ResponseEntity<Map> saveRegisterManual(@RequestBody
RegisterModel objModel) throws RuntimeException {
Map map = new HashMap();
User user =
userRepository.checkExistingEmail(objModel.getEmail());
if (null != user) {
return new
ResponseEntity<Map>(templateCRUD.notFound("Username sudah ada"),
HttpStatus.OK);
}
map = serviceReq.registerManual(objModel);
Allow URI:
Run postman :
30
b. Register By OTP
https://gitlab.com/binarxgrab_mainbootcamp/backend_java/boilerplate_bej
Branch : 04032022-register-otp
Step 2 : Pom.xml
<!-- email -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
Step 3 :application.properties
31
expired.token.password.minute=1200
Step 4 : UserServiceImpl.registerManual
Uncoment baris
user.setEnabled(false); //
@Autowired
public EmailSender emailSender;
// Step 2: sendp OTP berupa URL: guna updeta enable agar bisa login:
@PostMapping("/send-otp")//send OTP
public Map sendEmailegister(@RequestBody RegisterModel user) {
String message = "Thanks, please check your email for
activation.";
32
calendar.setTime(dateNow);
calendar.add(Calendar.MINUTE, expiredToken);
Date expirationDate = calendar.getTime();
found.setOtp(otp);
found.setOtpExpiredDate(expirationDate);
template = template.replaceAll("\\{\\{USERNAME}}",
(found.getFullname() == null ? found.getUsername1() :
found.getFullname()));
template = template.replaceAll("\\{\\{VERIFY_TOKEN}}", otp);
userRepository.save(found);
} else {
template = template.replaceAll("\\{\\{USERNAME}}",
(found.getFullname() == null ? found.getUsername1() :
found.getFullname()));
template = template.replaceAll("\\{\\{VERIFY_TOKEN}}",
found.getOtp());
}
emailSender.sendAsync(found.getUsername(), "Register", template);
return templateCRUD.templateSukses(message);
}
}
Step 6 : utils
package com.binar.grab.util;
import org.springframework.stereotype.Component;
@Component("emailTemplate")
public class EmailTemplate {
public String getRegisterTemplate() {
return "<!DOCTYPE html>\n" +
"<html>\n" +
"<head>\n" +
"<style>\n" +
"\t.email-container {\n" +
"\t\tpadding-top: 10px;\n" +
"\t}\n" +
"\tp {\n" +
"\t\ttext-align: left;\n" +
"\t}\n" +
"\n" +
"\ta.btn {\n" +
"\t\tdisplay: block;\n" +
"\t\tmargin: 30px auto;\n" +
"\t\tbackground-color: #01c853;\n" +
"\t\tpadding: 10px 20px;\n" +
"\t\tcolor: #fff;\n" +
"\t\ttext-decoration: none;\n" +
"\t\twidth: 30%;\n" +
"\t\ttext-align: center;\n" +
"\t\tborder: 1px solid #01c853;\n" +
"\t\ttext-transform: uppercase;\n" +
"\t}\n" +
"\ta.btn:hover,\n" +
"\ta.btn:focus {\n" +
33
"\t\tcolor: #01c853;\n" +
"\t\tbackground-color: #fff;\n" +
"\t\tborder: 1px solid #01c853;\n" +
"\t}\n" +
"\t.user-name {\n" +
"\t\ttext-transform: uppercase;\n" +
"\t}\n" +
"\t.manual-link,\n" +
"\t.manual-link:hover,\n" +
"\t.manual-link:focus {\n" +
"\t\tdisplay: block;\n" +
"\t\tcolor: #396fad;\n" +
"\t\tfont-weight: bold;\n" +
"\t\tmargin-top: -15px;\n" +
"\t}\n" +
"\t.mt--15 {\n" +
"\t\tmargin-top: -15px;\n" +
"\t}\n" +
"</style>\n" +
"</head>\n" +
"<body>\n" +
"\t<div class=\"email-container\">\n" +
"\t\t<p>Halo <span class=\"user-
name\">{{USERNAME}}</span> Selamat bergabung</p>\n" +
"\t\t<p>Harap konfirmasikan email kamu dengan memasukan
kode dibawah ini</p>\n" +
"\t\t\n" +
"\t\tkode: <b>{{VERIFY_TOKEN}}</b>\n" +
"\t\t\n" +
"\t\t<p class=\"mt--15\">Jika kamu butuh bantuan atau
pertanyaan, hubungi customer care kami di .... atau kirim email
ke ....</p>\n" +
"\t\t\n" +
"\t\t<p>Semoga harimu menyenangkan!</p>\n" +
"\t\t\n" +
"\t\t<p>PT ABC,</p>\n" +
"\t\t<p class=\"mt--15\".....</p>\n" +
"\n" +
"\t\t\n" +
"\t</div>\n" +
"</body>\n" +
"</html>";
}
package com.binar.grab.util;
import java.util.Random;
34
@SuppressWarnings("SpellCheckingInspection")
public static String randomString(int size, boolean numberOnly) {
String saltChars = "1234567890";
if (!numberOnly) {
saltChars +=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
}
StringBuilder salt = new StringBuilder();
Random rnd = new Random();
while (salt.length() < size) {
int index = (int) (rnd.nextFloat() * saltChars.length());
salt.append(saltChars.charAt(index));
}
return salt.toString();
}
package com.binar.grab.service.email;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.TaskExecutor;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.mail.internet.MimeMessage;
@SuppressWarnings({"WeakerAccess", "ConstantConditions"})
@Component("emailSender")
public class EmailSender {
private final static Logger logger =
LoggerFactory.getLogger(EmailSender.class);
@Autowired
private JavaMailSenderImpl mailSender;
@Value("${spring.mail.sender.name:}")
private String senderName;
@Value("${spring.mail.sender.mail:}")
private String senderEmail;
@Qualifier("taskExecutor")
@Autowired
private TaskExecutor taskExecutor;
35
public boolean send(String from, String email, String subject, String
message) {
MimeMessage mime = mailSender.createMimeMessage();
if (StringUtils.isEmpty(from)) {
from = senderEmail;
}
if (StringUtils.isEmpty(from)) {
from = "admin@mail.com";
}
boolean success = false;
try {
logger.info("Sending email to: "+email);
logger.info("Sending email from: "+from);
logger.info("Sending email with subject: "+subject);
return success;
}
Postman :
36
c. Konfirmasi OTP Token User: guna untuk mengupdate user agar
dapat login
Controller
@GetMapping("/register-confirm-otp/{token}")
public ResponseEntity<Map> saveRegisterManual(@PathVariable(value =
"token") String tokenOtp) throws RuntimeException {
String dateToken =
config.convertDateToString(user.getOtpExpiredDate());
if(Long.parseLong(today) > Long.parseLong(dateToken)){
return new ResponseEntity<Map>(templateCRUD.templateEror("Your
token is expired. Please Get token again."), HttpStatus.OK);
}
//update user
user.setEnabled(true);
userRepository.save(user);
Config
public String convertDateToString(Date date) {
d. Forget Password
Gitlab :
https://gitlab.com/binarxgrab_mainbootcamp/backend_java/boilerplate_bej
Branch: 08032022-forget-password
Step 1 : Chek email – jika tidak ada maka return email tidak ditemukan
Step 2: Jika email ditemukan – OTP Akan dikirim ke email : Validasi OTP
Step 3: User akan memasukkan OTP untuk dapat melakukan reset Password
import com.binar.grab.config.Config;
import com.binar.grab.dao.request.ResetPasswordModel;
import com.binar.grab.model.oauth.User;
38
import com.binar.grab.repository.oauth.UserRepository;
import com.binar.grab.service.UserService;
import com.binar.grab.service.email.EmailSender;
import com.binar.grab.util.EmailTemplate;
import com.binar.grab.util.SimpleStringUtils;
import com.binar.grab.util.TemplateResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.StringUtils;
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;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/forget-password/")
public class ForgerPasswordController {
@Autowired
private UserRepository userRepository;
@Autowired
public UserService serviceReq;
@Value("${expired.token.password.minute:}")//FILE_SHOW_RUL
private int expiredToken;
@Autowired
public TemplateResponse templateCRUD;
@Autowired
public EmailTemplate emailTemplate;
@Autowired
public EmailSender emailSender;
@Autowired
private PasswordEncoder passwordEncoder;
if (StringUtils.isEmpty(user.getEmail())) return
templateCRUD.templateEror("No email provided");
User found = userRepository.findOneByUsername(user.getEmail());
if (found == null) return templateCRUD.notFound("Email not found");
//throw new BadRequest("Email not found");
39
if (StringUtils.isEmpty(found.getOtp())) {
User search;
String otp;
do {
otp = SimpleStringUtils.randomString(6, true);
search = userRepository.findOneByOTP(otp);
} while (search != null);
Date dateNow = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(dateNow);
calendar.add(Calendar.MINUTE, expiredToken);
Date expirationDate = calendar.getTime();
found.setOtp(otp);
found.setOtpExpiredDate(expirationDate);
template = template.replaceAll("\\{\\{PASS_TOKEN}}", otp);
template = template.replaceAll("\\{\\{USERNAME}}",
(found.getUsername1() == null ? "" +
"@UserName"
:
"@" + found.getUsername1()));
userRepository.save(found);
} else {
template = template.replaceAll("\\{\\{USERNAME}}",
(found.getUsername1() == null ? "" +
"@UserName"
:
"@" + found.getUsername1()));
template = template.replaceAll("\\{\\{PASS_TOKEN}}",
found.getOtp());
}
emailSender.sendAsync(found.getUsername(), "Chute - Forget
Password", template);
return templateCRUD.templateSukses("success");
return templateCRUD.templateSukses("Success");
}
40
config.isRequired);
if (model.getNewPassword() == null) return
templateCRUD.notFound("New Password " + config.isRequired);
User user = userRepository.findOneByOTP(model.getOtp());
String success;
if (user == null) return templateCRUD.notFound("Token not valid");
user.setPassword(passwordEncoder.encode(model.getNewPassword().replaceAll("
\\s+", "")));
user.setOtpExpiredDate(null);
user.setOtp(null);
try {
userRepository.save(user);
success = "success";
} catch (Exception e) {
return templateCRUD.templateEror("Gagal simpan user");
}
return templateCRUD.templateSukses(success);
}
Model Request
package com.binar.grab.dao.request;
import lombok.Data;
@Data
public class ResetPasswordModel {
public String email;
Email Template
public String getResetPassword(){
return "<!DOCTYPE html>\n" +
"<html>\n" +
"<head>\n" +
"<style>\n" +
"\t.email-container {\n" +
"\t\tpadding-top: 10px;\n" +
"\t}\n" +
"\tp {\n" +
"\t\ttext-align: left;\n" +
"\t}\n" +
"\n" +
41
"\ta.btn {\n" +
"\t\tdisplay: block;\n" +
"\t\tmargin: 30px auto;\n" +
"\t\tbackground-color: #01c853;\n" +
"\t\tpadding: 10px 20px;\n" +
"\t\tcolor: #fff;\n" +
"\t\ttext-decoration: none;\n" +
"\t\twidth: 30%;\n" +
"\t\ttext-align: center;\n" +
"\t\tborder: 1px solid #01c853;\n" +
"\t\ttext-transform: uppercase;\n" +
"\t}\n" +
"\ta.btn:hover,\n" +
"\ta.btn:focus {\n" +
"\t\tcolor: #01c853;\n" +
"\t\tbackground-color: #fff;\n" +
"\t\tborder: 1px solid #01c853;\n" +
"\t}\n" +
"\t.user-name {\n" +
"\t\ttext-transform: uppercase;\n" +
"\t}\n" +
"\t.manual-link,\n" +
"\t.manual-link:hover,\n" +
"\t.manual-link:focus {\n" +
"\t\tdisplay: block;\n" +
"\t\tcolor: #396fad;\n" +
"\t\tfont-weight: bold;\n" +
"\t\tmargin-top: -15px;\n" +
"\t}\n" +
"\t.mt--15 {\n" +
"\t\tmargin-top: -15px;\n" +
"\t}\n" +
"</style>\n" +
"</head>\n" +
"<body>\n" +
"\t<div class=\"email-container\">\n" +
"\t\t<p>If you Requested a password for {{USERNAME}}, use the
confirmation code below to complete the process. If you didn't make this
request, ignore the email.</p>\n" +
"\t\t\n" +
"\t\tCode: <b>{{PASS_TOKEN}}</b>\n" +
"\t\t\n" +
"\t\t\n" +
"\t\t<p>Regards</p>\n" +
"\t\t<p>PT ABC</p>\n" +
"\t\t<p class=\"mt--15\".....</p>\n" +
"\n" +
"\t\t\n" +
"\t</div>\n" +
"</body>\n" +
"</html>";
}
Allow URI : tambahkan uri yang digunakan agar diaskes tampa token
42
b. Test Postman
Step 1
43
c. Filter And Sorting
Controller
package com.binar.grab.controller;
import com.binar.grab.config.Config;
import com.binar.grab.model.Barang;
import com.binar.grab.model.Supplier;
import com.binar.grab.model.oauth.User;
import com.binar.grab.repository.BarangRepository;
import com.binar.grab.repository.oauth.UserRepository;
import com.binar.grab.service.SupplierService;
import com.binar.grab.service.oauth.Oauth2UserDetailsService;
import com.binar.grab.util.SimpleStringUtils;
import com.binar.grab.util.TemplateResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetails;
import
org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import com.binar.grab.service.BarangService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
44
@RestController
@RequestMapping("/v1/binar/barang")
public class BarangController {
@Autowired
public BarangService barangService;
@Autowired
private Oauth2UserDetailsService userDetailsService;
@Autowired
public BarangRepository barangRepository;
@Autowired
public TemplateResponse templateResponse;
@Autowired
public UserRepository userRepository;
@GetMapping("list-barang")// by seller
public ResponseEntity<Map> listNotif(
@RequestParam() Integer page,
@RequestParam(required = true) Integer size,
@RequestParam(required = false) String nama,// BUY OR SELL :
menentukan siapa yang akses
@RequestParam(required = false) Double priceMin,
@RequestParam(required = false) Double priceMax,
@RequestParam(required = false) String orderby,
@RequestParam(required = false) String ordertype,
Principal principal) {
/*
1.principal : mendapatkan username berdasarkan token user yang akses di
client
2. getShort : shoritng
*/
Pageable show_data = simpleStringUtils.getShort(orderby, ordertype,
page, size);
Page<Barang> list = null;
45
} else {
list = barangRepository.getAllData(show_data);
}
return new ResponseEntity<Map>(templateResponse.templateSukses(list),
new HttpHeaders(), HttpStatus.OK);
}
if (null == user) {
throw new UsernameNotFoundException("User not found");
}
User idUser = userRepository.findOneByUsername(user.getUsername());
if (null == idUser) {
throw new UsernameNotFoundException("User name not found");
}
return idUser;
46
Tambahkan SimpleStringUtils
public Pageable getShort(String orderby, String ordertype, Integer page,
Integer size) {
Pageable show_data;
if (orderby != null) {
if (ordertype != null) {
if (ordertype.equals("desc")) {
show_data = PageRequest.of(page, size,
Sort.by(orderby).descending());
} else {
show_data = PageRequest.of(page, size,
Sort.by(orderby).ascending());
}
} else {
show_data = PageRequest.of(page, size,
Sort.by(orderby).descending());
}
} else {
show_data = PageRequest.of(page, size, Sort.by("id").descending());
}
return show_data;
}
Repository
public Page<Barang> findByNamaLike(String nama , Pageable pageable);
d. File Handling
Branch : 11032022-file-handling
a. Upload File
REQUEST Req Service api: Response :
File 1 or Simpan File URL
Mnay FIle DOWNLO
Res
47
b. Service api
Aplication.properties
spring.servlet.multipart.max-file-size=5MB
spring.servlet.multipart.max-request-size=5MB
app.uploadto.cdn=./cdn/ atau app.uploadto.cdnD://folder/subfolder
Class
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
48
@RestController
//@EnableCaching
public class FileController {
private static final Logger logger =
LoggerFactory.getLogger(FileController.class);
@Value("${app.uploadto.cdn}")//FILE_SHOW_RUL
private String UPLOADED_FOLDER;
@Autowired
private FileStorageService fileStorageService;
try {
} catch (Exception e) {
e.printStackTrace();
return new UploadFileResponse(fileName, null,
file.getContentType(), file.getSize(), e.getMessage());
}
String fileDownloadUri =
ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/v1/showFile/")
.path(fileNameforDOwnload)
.toUriString();
49
// try {
// if (type.toLowerCase().equals("product")) {
// File theDir = new File(UPLOADED_FOLDER_PRODUCT);
// if (!theDir.exists()) {
// theDir.mkdirs();
// return UPLOADED_FOLDER_PRODUCT;
// }
// return UPLOADED_FOLDER;
// }
//
// } catch (Exception e) {
// logger.info("Eror create folder" + e);
// }
// return UPLOADED_FOLDER;
// }
@GetMapping("v1/showFile/{fileName:.+}")
public ResponseEntity<Resource> showFile(@PathVariable String fileName,
HttpServletRequest request) { // Load file as Resource : step 1 load path
lokasi name file
Resource resource =
fileStorageService.loadFileAsResource(fileName);
// Try to determine file's content type
String contentType = null;
try {
// dapatan URL download
// System.out.println("resource.getFile().getAbsolutePath" +
resource.getFile().getAbsolutePath());
contentType =
request.getServletContext().getMimeType(resource.getFile().getAbsolutePath(
));
@PostMapping("v1/uploadMultipleFiles")
public List<UploadFileResponse>
uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) throws
IOException {
return Arrays.asList(files)
.stream()
.map(file -> {
50
try {
// step 1 : call method uplaod
return uploadFile(file);
} catch (IOException e) {
e.printStackTrace();
}
return null;
})
.collect(Collectors.toList());
}
try {
logger.info(String.format("Saving uploaded file to: '%s'",
file.getAbsolutePath()));
FileOutputStream fos = new FileOutputStream(file);
fos.write(upload.getBytes());
fos.close();
} catch (IOException e) {
logger.error(String.format("Error: POST|UPLOAD %s", routeName),
e);
}
return file;
}
package com.binar.grab.controller.fileupload;
package com.binar.grab.controller.fileupload;
51
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
@Value("${app.uploadto.cdn}")//FILE_SHOW_RUL
private String uploadDir;
package com.binar.grab.controller.fileupload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.Date;
@Service
public class FileStorageService {
@Autowired
public FileStorageService(FileStorageProperties fileStorageProperties)
{
this.fileStorageLocation =
Paths.get(fileStorageProperties.getUploadDir())
.toAbsolutePath().normalize();
try {
Files.createDirectories(this.fileStorageLocation);
} catch (Exception ex) {
52
throw new FileStorageException("Could not create the directory
where the uploaded files will be stored.", ex);
}
}
package com.binar.grab.controller.fileupload;
53
import lombok.Data;
@Data
public class UploadFileResponse {
private String fileName;
private String fileDownloadUri;
private String fileType;
private long size;
private String eror;
try {
} catch (Exception e) {
54
e.printStackTrace();
return new UploadFileResponse(fileNameforDOwnload, null,
file.getContentType(), file.getSize(), e.getMessage());
}
String fileDownloadUri =
ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/v1/showFile/")
.path(fileNameforDOwnload)
.toUriString();
@EnableConfigurationProperties({
FileStorageProperties.class
})
55
e. Testing
Upload 1 file
56
Upload many file
57
Download file
package com.binar.grab.controller.fileupload;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import
org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
58
//step 1
@Configuration
public class ResourceConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods()
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE",
"PATCH");
}
registry.addResourceHandler("/showFile/**").addResourceLocations("file:cdn/
");
}
}
Testing
localhost:9090/api/showFile/namafile.png:
59
b. Buat Akun Github
https://github.com/login
deploy project
step 1: git reset
step 2 : git init
step 3 : git add .
step 4 : git commit -m "first commit"
step 5 : git branch -M master
step 6 :
masuk disini untuk generate token
https://github.com/settings/tokens
git remote add origin https://github.com/rikialdi/binar_grab.git
or
git remote set-url origin git@github.com:rikialdi/binar_grab.git
60
Tampilan setelah membuat aplikasi, jika ingin mengubungkan ke github,
silahkan pilih github seperti gambar dibawah ini
61
Pilih tampil repo, Akan tampil semua repo pada akun github teman2
62
Paga gambar diatas, dapat dilihat pada tombol enable autometic deploy,
digunakan untuk deploy otomatis mengunakan ci-cd.
Atau degan deploy manual dengan menekan tombol deploy branch.
63
Silahkan klik salah satu database anda
Pada gambar diatas menjelaskan limit untuk database posgre gratis di heroku.
Silahkan klik tombol dibawah ini untuk melihat credential database , tujuan agar
bisa diakses remote oleh aplikasi local kita.
64
Berikut tampilan credential
66
Step 3- isi file
stages: # List of stages for jobs, and their order of execution
- build
- test
- deploy
build-job: # This job runs in the build stage, which runs first.
image: maven:3-jdk-8
stage: build
script:
- mvn package
only:
refs:
- master
67
script:
- echo "Deploying application..."
- echo "Application successfully deployed."
#stages:
# - build
# - deploy
#build:
# image: maven:3-jdk-8
# stage: "Build"
# script:
# - mvn package
# # when: manual
# artifacts:
# name: "$CI_JOB_STAGE-$CI_COMMIT_REF_NAME"
# paths:
# - target/*.jar
# only:
# refs:
# - master
production:
image: ruby:2.4
stage: deploy
before_script:
- gem install dpl
- wget -qO- https://cli-assets.heroku.com/install-ubuntu.sh | sh
script:
- dpl --provider=heroku --app=binar-test --api-key=f4b7b3e5-20
- export HEROKU_API_KEY=f4b7b3e5-2034-469f-
- heroku run --app ecomerced2r migrate
environment:
name: production
url: https://binar-test.herokuapp.com
68
h. Register OTP by URI Tymeleaf
Branch : register-tymeleaf
https://github.com/rikialdi/binar_grab.git
a. Step 1-Controller
Masih menggunakan class controller register
@Value("${BASEURL:}")//FILE_SHOW_RUL
private String BASEURL;
@PostMapping("/register-tymeleaf")
public ResponseEntity<Map> saveRegisterManualTymeleaf(@RequestBody
RegisterModel objModel) throws RuntimeException {
Map map = new HashMap();
}
map = serviceReq.registerManual(objModel);
@PostMapping("/send-otp-tymeleaf")//send OTP
public Map sendEmailegisterTymeleaf(@RequestBody RegisterModel user) {
69
String message = "Thanks, please check your email for activation.";
found.setOtp(otp);
found.setOtpExpiredDate(expirationDate);
template = template.replaceAll("\\{\\{USERNAME}}",
(found.getFullname() == null ? found.getUsername1() :
found.getFullname()));
template = template.replaceAll("\\{\\{VERIFY_TOKEN}}", BASEURL +
"user-register/web/index/"+ otp);
userRepository.save(found);
} else {
template = template.replaceAll("\\{\\{USERNAME}}",
(found.getFullname() == null ? found.getUsername1() :
found.getFullname()));
template = template.replaceAll("\\{\\{VERIFY_TOKEN}}", BASEURL +
"user-register/web/index/"+ found.getOtp());
}
emailSender.sendAsync(found.getUsername(), "Register", template);
return templateCRUD.templateSukses(message);
}
b. Step 2- Apllication.properties
BASEURL=http://localhost:8082/api/
70
c. Step 3- Controller Tymeleaf
package com.binar.grab.controller.tymeleaf;
import com.binar.grab.config.Config;
import com.binar.grab.model.oauth.User;
import com.binar.grab.repository.oauth.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.security.Principal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
@Controller
@RequestMapping("/user-register/web/")
public class RegisterConfim {
@Autowired
public UserRepository userRepo;
@GetMapping(value = { "/index/{tokenotp}"})
public String index(Model model,@PathVariable String tokenotp) {
User user = userRepo.findOneByOTP(tokenotp);
if (null == user) {
System.out.println("user null: tidak ditemukan");
model.addAttribute("erordesc", "User not found for code
"+tokenotp);
model.addAttribute("title", "");
return "register";
}
String today = convertDateToString(new Date());
71
String dateToken =
config.convertDateToString(user.getOtpExpiredDate());
if(Long.parseLong(today) > Long.parseLong(dateToken)){
model.addAttribute("erordesc", "Your token is expired. Please
Get token again.");
model.addAttribute("title", "");
return "register";
}
user.setEnabled(true);
userRepo.save(user);
model.addAttribute("title", "Congratulations, "+user.getUsername()
+", you have successfully registered.");
model.addAttribute("erordesc", "");
return "register";
}
@GetMapping(value = "/user1")
public String index() {
return "user";
}
@RequestMapping(value = "/user")
public Principal user(Principal principal) {
return principal;
}
d. Step 4- UI Tymeleaf
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title th:utext="${title}"/>
<link rel="stylesheet" type="text/css" th:href="@{/css/style.css}"/>
</head>
<body>
<h1 th:utext="${title}"/>
<h1 th:utext="${erordesc}"/>
<br/><br/>
<div>Copyright 2022</div>
</body>
</html>
72
e. Test Run Postman
73
Step 3-klik url yang dikirimkan di email, dan akan muncul tampilan berikut :
f. CronJob Scheduler
Branch : cronjob
https://github.com/rikialdi/binar_grab/
74
b. Config
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class ThreadConfiguration {
// https://rizkimufrizal.github.io/belajar-spring-async/
/*
https://stackoverflow.com/questions/17659510/core-pool-size-vs-maximum-
pool-size-in-threadpoolexecutor
https://www.baeldung.com/java-threadpooltaskexecutor-core-vs-max-
poolsize
*/
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("default_task_executor_thread");
executor.initialize();
return executor;
}
/*
Code diatas berfungsi untuk mendefinisikan bean asyncTaskExecutor,
dimana penulis membuat sebuah thread pool dengan prefix
default_task_executor_thread,
sehingga nanti nya kita dapat menggunakan prefix tersebut untuk
memanggil
thread pool nya, contoh penggunaan nya yaitu
default_task_executor_thread-test atau default_task_executor_thread-sample.
Ambil contoh ini. Ukuran kumpulan utas awal adalah 1, ukuran kumpulan inti
adalah 5, ukuran kumpulan maks adalah 10 dan antrian adalah 100.
Sun's way: saat request masuk thread akan dibuat hingga 5, kemudian tugas
akan ditambahkan ke antrian hingga mencapai 100. Ketika antrian penuh akan
dibuat thread baru hingga maxPoolSize. Setelah semua utas digunakan dan
antrean penuh, tugas akan ditolak. Saat antrian berkurang, begitu juga
jumlah utas aktif.
Cara yang diantisipasi pengguna: saat permintaan masuk dalam utas akan
dibuat hingga 10, maka tugas akan ditambahkan ke antrean hingga mencapai
100 pada titik mana mereka ditolak. Jumlah utas akan diganti namanya secara
maksimal hingga antrian kosong. Ketika antrian kosong, utas akan mati
sampai ada yang corePoolSizetersisa.
75
awal dan ingin antrian menjadi lebih kecil, sedangkan metode Sun ingin
menjaga ukuran kolam tetap kecil dan hanya menambahnya setelah beban
menjadi banyak.
Jika jumlah utas kurang dari corePoolSize, buat utas baru untuk menjalankan
tugas baru.
Jika jumlah utas sama (atau lebih besar dari) corePoolSize, masukkan tugas
ke dalam antrian.
Jika antrian penuh, dan jumlah utas kurang dari maxPoolSize, buat utas baru
untuk menjalankan tugas.
Jika antrian penuh, dan jumlah utas lebih besar dari atau sama dengan
maxPoolSize, tolak tugas. Panjang dan pendeknya adalah bahwa utas baru
hanya dibuat ketika antrian terisi, jadi jika Anda menggunakan antrian
tidak terbatas maka jumlah utas tidak akan melebihi corePoolSize.
Untuk penjelasan lebih lengkap, dapatkan dari mulut kuda:
ThreadPoolExecutordokumentasi API.
Ada posting forum yang sangat bagus yang memberi tahu Anda cara
ThreadPoolExecutorkerjanya dengan contoh kode:
http://forums.sun.com/thread.jspa?threadID=5401400&tstart=0
c. Main Application
d. Cronjob Example
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
76
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
@Component
public class CronJobExample {
@Scheduled(cron = "${cron.expression:-}")
public void sendAsync() {
taskExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("Method executed at every 2 weeks.
Current time is :: " + new Date());
}
});
}
}
a. Step 1
Tambahkan file berikut
77
b. Step 2 : .env
APP_ENV=dev
DB_USER=postgres
DB_PASS=postgres
DB_NAME=postgres
DB_HOST=postgres
DB_PORT=5432
REDIS_HOST=redis
UID=1000
GROUPS=1000
CDN_PATH=/opt/app/cache/cdn/
CDN_BASE_URL=http://localhost:8082/cdn/
CRON_EXPRESSION=0 0 */14 * * ?
CRON_EXPRESSION_USER=0 1 * * * ?
c. Step 3 : docker-compose.yml
version: '3.6'
networks:
grab: {}
services:
# nginx:
# image: nginx
# ports:
# - 8090:80
# volumes:
# - ./nginx/nginx-njs.conf:/etc/nginx/nginx.conf:ro
# - ./nginx/config/cors.conf:/etc/nginx/config/cors.conf:ro
# - ./nginx/route.conf:/etc/nginx/routes/bca.conf:ro
78
# - ./nginx/config/nginx-njs.js:/etc/nginx/javascript/nginx.js:ro
# - ./nginx/site-with-oauth2.conf:/etc/nginx/conf.d/default.conf:ro
# - ./nginx/www:/var/www/html:ro
# - ./cache/cdn:${CDN_PATH:-/opt/app/cache/cdn}:ro
# networks:
# - binar-postgres
postgres:
image: postgres:9.5
volumes:
- ./cache/postgres:/var/lib/postgresql/data
#user: ${UID:-1000}:${GROUPS:-1000}
environment:
- POSTGRES_USER=${DB_USER:-postgres}
- POSTGRES_PASSWORD=${DB_PASS:-postgres}
- POSTGRES_DB=${DB_NAME:-postgres}
- PGDATA=/var/lib/postgresql/data/data
- TZ=Asia/Jakarta
ports:
- 5436:5432
networks:
grab:
aliases:
- ${DB_HOST:-postgres}
# redis:
# image: redis
# volumes:
# - ./cache/redis:/data
## user: ${uid:-1000}:${groups:-1000}
# networks:
# grab:
# aliases:
# - ${redis_host:-redis}
spring:
image: maven:3-jdk-8
ports:
- 8090:8082
# restart: unless-stopped
working_dir: /opt/app
volumes:
- .:/opt/app
# - ./cache/maven:/var/maven/.m2
# - ./cache/cdn:${CDN_PATH:-/opt/app/cache/cdn}
#user: ${UID:-1000}:${GROUPS:-1000}
environment:
- TZ=Asia/Jakarta
- SPRING_DATASOURCE_URL=jdbc:postgresql://ec2-18-210-191-5.compute-
1.amazonaws.com:5432/dvch7rlkgrl4i
- SPRING_DATASOURCE_USERNAME=ydtyecrssfbazz
-
SPRING_DATASOURCE_PASSWORD=efe7049a7247bef02e99208410c2f2ad038e2a29e1b94c29
5d2af0aaca7d8a40
- SPRING_PROFILES_CDN_PATHACTIVE=${APP_ENV:-dev}
# - SPRING_CACHE_TYPE=redis
# - SPRING_REDIS_HOST=${REDIS_HOST:-redis}
# - SPRING_REDIS_PORT=${REDIS_PORT:-6379}
# - SPRING_CACHE_REDIS_TIMETOLIVE=1000
79
- SECURITY_JWT_ENABLED=true
- SERVER_SERVLET_CONTEXT_PATH=/api
- MAVEN_CONFIG=/var/maven/.m2
- FILE_UPLOADDIR=${CDN_PATH:-/opt/app/cache/cdn/}
- FILE_SHOW_URL=${CDN_BASE_URL:-http://172.20.2.153:8090/cdn/}
- APP_UPLOADTO_CDN=${CDN_PATH:-/opt/app/cache/cdn/}
# - "MAVEN_OPTS=-Xmx2048m -XX:MaxPermSize=500m"
# - CRON_EXPRESSION=${CRON_EXPRESSION:-0 0 1 */14 * ?}
# - CRON_EXPRESSION_USER=${CRON_EXPRESSION_USER:-0 1 * * * ?}
networks:
grab:
aliases:
- oauth2.api
- hr.api
command: [ "mvn", "-Duser.home=/var/maven", "spring-boot:run" ]
#command: [ "mvn", "-Duser.home=/var/maven", "spring-boot:run" ]
80
d. Step 4: run and noted
f. remote images :
https://stackoverflow.com/questions/37971961/docker-error-bind-
address-already-in-use
pass: 12345
81
Github : https://github.com/rikialdi/binar_grab/
a. Step 1 – strukture
b. Step 2 :code
ApiConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import
org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean;
@Configuration
public class ApiConfig {
@Primary
@Bean
public FreeMarkerConfigurationFactoryBean factoryBean() {
FreeMarkerConfigurationFactoryBean bean=new
FreeMarkerConfigurationFactoryBean();
bean.setTemplateLoaderPath("classpath:/templates");
return bean;
}
EmailService
package com.binar.grab.service.email.email;
import com.binar.grab.config.Config;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSender;
82
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import javax.activation.DataSource;
import javax.imageio.ImageIO;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
@Service
public class EmailService {
@Autowired
private JavaMailSender sender;
// @Autowired
// private JavaMailSenderImpl sender;
@Autowired
private Configuration config;
@Value("${spring.mail.username:}")//FILE_SHOW_RUL
private String emailPengirim;
83
fileName.substring(fileName.lastIndexOf(".") );
helper.addAttachment(config2.convertDateToString(new Date())
+"image4"+formatFile, file2);
Template t = config.getTemplate("email-template.ftl");
String html =
FreeMarkerTemplateUtils.processTemplateIntoString(t, model);
helper.setTo(request.getTo());
// helper.setTo(emailPengirim);
helper.setText(html, true);
helper.setSubject(request.getSubject());
helper.setFrom(request.getFrom());
sender.send(message);
return response;
}
MailRequest
package com.binar.grab.service.email.email;
import lombok.Data;
@Data
public class MailRequest {
MailResponse
package com.binar.grab.service.email.email;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
84
@AllArgsConstructor
@NoArgsConstructor
public class MailResponse {
private String message;
private boolean status;
Application.properties
BASEURL=http://localhost:8082/api/
SHOWFILEPATH=http://localhost:8082/api/showFile/
d. Step 4: resouces
Email-template
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Java Techie Mail</title>
</head>
</div>
<table style="border-bottom:2px dashed ">
<tr >
<th style="border-bottom:2px dashed
">Qty</th>
<th style="border-bottom:2px dashed
">Desc</th>
<th style="border-bottom:2px dashed
">Amt</th>
</tr>
<#list datainvoice as item>
<tr>
<td>${item.nama}</td>
<td>${item.satuan}</td>
<td>${item.harga}</td>
</tr>
<tr>
<td colspan="1">1</td>
<td colspan="1">Shipping</td>
<td colspan="1">10</td>
</tr>
<br>
</#list>
<tr>
<td colspan="2" style="text-align:
right;">SUBTOTAL</td>
<td colspan="1" >10000</td>
</tr>
86
</table>
<div style="font-size: 12px; color: #000000;">
<br> <b>Will be shipped to:</b><br>
Jl Sudirman <br>
Jakarta <br>
20876 <br>
</div>
<div style="font-size: 12px; color: #000000;">
<br> <li>You ill get notification on our app
<b>@UserTes</b> when ship
your items.</li><br>
</div>
</td>
</tr>
</table>
<br> <br></td>
</tr>
</table>
</body>
</html>
@Autowired
public BarangRepository barangRepository;
@Autowired
private EmailService service;
@Test
public void sendEmail( ) {
MailRequest request = new MailRequest();
request.setName("Invoice Test name");
request.setTo("rikialdipari@gmail.com");
request.setFrom("rikialdipari@gmail.com");
request.setSubject("Invoice Test");
Map<String, Object> model = new HashMap<>();
model.put("namesaya", "riki aldi pari");
model.put("chuteicon",
"https://binar-test.herokuapp.com/api/showFile/2432022025921Captureass.PNG"
);
BigDecimal total= new BigDecimal(0);
87
model.put("datainvoice", data.getContent());
model.put("iconuser","https://binar-test.herokuapp.com/api/showFile/
2432022025921Captureass.PNG") ;
MailResponse ma = service.sendEmail(request, model);
System.out.println("MailResponse 1="+ma.getMessage());
a. Step 1- pom.xml
<!--valdation-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
88
c. Step 3- user impl
@Value("${BASEURL}")
private String baseUrl;
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@Override
public Map login(LoginModel loginModel) {
/**
* bussines logic for login here
* **/
try {
Map<String, Object> map = new HashMap<>();
User checkUser =
repoUser.findOneByUsername(loginModel.getUsername());
if (response.getStatusCode() == HttpStatus.OK) {
89
User user =
repoUser.findOneByUsername(loginModel.getUsername());
List<String> roles = new ArrayList<>();
map.put("access_token",
response.getBody().get("access_token"));
map.put("token_type",
response.getBody().get("token_type"));
map.put("refresh_token",
response.getBody().get("refresh_token"));
map.put("expires_in",
response.getBody().get("expires_in"));
map.put("scope", response.getBody().get("scope"));
map.put("jti", response.getBody().get("jti"));
return map;
} else {
return templateResponse.notFound("user not found");
}
} catch (HttpStatusCodeException e) {
e.printStackTrace();
if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
return templateResponse.templateEror("invalid login");
}
return templateResponse.templateEror(e);
} catch (Exception e) {
e.printStackTrace();
return templateResponse.templateEror(e);
}
}
90
d. Model – request
package com.binar.grab.dao.request;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Data
public class LoginModel {
@NotEmpty(message = "username is required.")
private String username;
@NotEmpty(message = "password is required.")
private String password;
}
e. Config-allow uri
91
f. Controller
package com.binar.grab.controller;
import com.binar.grab.config.Config;
import com.binar.grab.dao.request.LoginModel;
import com.binar.grab.dao.request.RegisterModel;
import com.binar.grab.model.oauth.User;
import com.binar.grab.repository.oauth.UserRepository;
import com.binar.grab.service.UserService;
import com.binar.grab.service.email.EmailSender;
import com.binar.grab.util.EmailTemplate;
import com.binar.grab.util.SimpleStringUtils;
import com.binar.grab.util.TemplateResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import java.util.Map;
@RestController
@RequestMapping("/user-login/")
public class LoginController {
@Autowired
private UserRepository userRepository;
92
Config config = new Config();
@Autowired
public UserService serviceReq;
@Value("${expired.token.password.minute:}")//FILE_SHOW_RUL
private int expiredToken;
@Autowired
public TemplateResponse templateCRUD;
@PostMapping("/login")
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Map> login(@Valid @RequestBody LoginModel
objModel) {
Map map = serviceReq.login(objModel);
return new ResponseEntity<Map>(map, HttpStatus.OK);
}
g. Testing
@Autowired
private TestRestTemplate restTemplate;
@Test
public void restTemplateLogin2() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", "*/*");
headers.set("Content-Type", "application/json");
String bodyTesting = "{\n" +
" \"username\":\"rikialdipari@gmail.com\",\n" +
" \"password\":\"password\"\n" +
"}";
HttpEntity<String> entity = new HttpEntity<String>(bodyTesting,
headers);
93
ResponseEntity<String> exchange =
restTemplate.exchange("http://localhost:8082/api/user-login/login",
HttpMethod.POST, entity, String.class);
assertEquals(HttpStatus.OK, exchange.getStatusCode());
System.out.println("response =" + exchange.getBody());
}
Output-sukses
h. Refresh TOKEN
94
b. Step 2- to do refresh token
95
@PostMapping(value = "/uploadsimpanbarang/{idsupplier}",consumes =
{"multipart/form-data", "application/json"})
public ResponseEntity<Map> uploadFile(
@RequestParam(value="file", required = true) MultipartFile file,
@PathVariable(value = "idsupplier") Long idsupplier,
@RequestParam(value="nama", required = true) String nama,
@RequestParam(value="stok", required = true) int stok,
@RequestParam(value="satuan", required = true) String satuan,
@RequestParam(value="harga", required = true) double harga
) {
Date date = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("ddMyyyyhhmmss");
String strDate = formatter.format(date);
String fileName = UPLOADED_FOLDER + strDate +
file.getOriginalFilename();
String fileNameforDOwnload = strDate + file.getOriginalFilename();
Path TO = Paths.get(fileName);
Map map = new HashMap();
try {
Files.copy(file.getInputStream(), TO); // pengolahan upload
disini :
// insert barang
Barang b = new Barang();
b.setNama(nama);
b.setStok(stok);
b.setSatuan(satuan);
b.setHarga(harga);
b.setFileName(fileNameforDOwnload);
map = barangService.insert(b, idsupplier);
} catch (Exception e) {
e.printStackTrace();
return new
ResponseEntity<Map>(templateResponse.templateEror("eror"), HttpStatus.OK);
}
return new ResponseEntity<Map>(templateResponse.templateSukses(map),
HttpStatus.OK);
}
96
d. Output
a. Step 1- Pom.xml
<!--valdation-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!--digunakan untuk validasi password -->
<dependency>
<groupId>org.passay</groupId>
<artifactId>passay</artifactId>
<version>1.6.0</version>
</dependency>
97
b. Step 2-application properties
Buatlah file baru di resources:
98
but %2$s are required.
INSUFFICIENT_COMPLEXITY=Password meets %2$s complexity rules, but %3$s are
required.
INSUFFICIENT_COMPLEXITY_RULES=No rules have been configured for a password
of length %1$s.
SOURCE_VIOLATION=Password cannot be the same as your %1$s password.
TOO_LONG=Password must be no more than %2$s characters in length.
TOO_SHORT=Password must be %1$s or more characters in length.
TOO_MANY_OCCURRENCES=Password contains %2$s occurrences of the character
'%1$s', but at most %3$s are allowed.
Class PasswordValueMatch
package com.binar.grab.controller.validationpass.annotation;
import
com.binar.grab.controller.validationpass.utils.PasswordFieldsValueMatchVali
dator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* <h2>PasswordValueMatch</h2>
*
* @author aek
*/
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordFieldsValueMatchValidator.class)
@Documented
public @interface PasswordValueMatch {
99
String message() default "Fields values don't match!";
String field();
String fieldMatch();
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@interface List {
PasswordValueMatch[] value();
}
}
Class ValidPassword
package com.binar.grab.controller.validationpass.annotation;
import
com.binar.grab.controller.validationpass.utils.PasswordConstraintValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* <h2>ValidPassword</h2>
*
* @author aek
*/
@Documented
@Constraint(validatedBy = PasswordConstraintValidator.class)
@Target({ TYPE, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface ValidPassword {
Class PasswordConstraintValidator
100
package com.binar.grab.controller.validationpass.utils;
import com.binar.grab.controller.validationpass.annotation.ValidPassword;
import lombok.SneakyThrows;
import org.passay.*;
import org.passay.PropertiesMessageResolver;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
/**
* <h2>PasswordConstraintValidator</h2>
*
* @author aek
*/
public class PasswordConstraintValidator implements
ConstraintValidator<ValidPassword, String> {
@Override
public void initialize(final ValidPassword arg0) {
@SneakyThrows
@Override
public boolean isValid(String password, ConstraintValidatorContext
context) {
101
// no whitespace= tidak dengan spasi
new WhitespaceRule(),
if (result.isValid()) {
return true;
}
Class PasswordFieldsValueMatchValidator
package com.binar.grab.controller.validationpass.utils;
import
com.binar.grab.controller.validationpass.annotation.PasswordValueMatch;
import org.springframework.beans.BeanWrapperImpl;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* <h2>PasswordFieldsValueMatchValidator</h2>
*
* @author aek
*/
public class PasswordFieldsValueMatchValidator implements
ConstraintValidator<PasswordValueMatch, Object> {
102
public void initialize(PasswordValueMatch constraintAnnotation) {
this.field = constraintAnnotation.field();
this.fieldMatch = constraintAnnotation.fieldMatch();
this.message = constraintAnnotation.message();
}
if (!isValid) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate(message)
.addPropertyNode(field)
.addConstraintViolation();
context
.buildConstraintViolationWithTemplate(message)
.addPropertyNode(fieldMatch)
.addConstraintViolation();
}
return isValid;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class ApiResponse {
103
}
}
Class BaseExceptionHandler
package com.binar.grab.controller.validationpass.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse
handleValidationExceptions(MethodArgumentNotValidException ex) {
ex.getBindingResult().getFieldErrors().forEach(error -> {
if (errors.containsKey(error.getField())) {
errors.put(error.getField(), String.format("%s,
%s", errors.get(error.getField()), error.getDefaultMessage()));
} else {
errors.put(error.getField(),
error.getDefaultMessage());
}
}
);
return new ApiResponse(errors, "VALIDATION_FAILED");
}
}
104
e. Step 4 – cara menggunakan method validation password ?
Misal api yang digunakan adalah : api register, ingin validasi password
Pada controller register, tambhakn text yang di kotak merah dibawah ini :
105
f. Tester
Failed:
Success:
106
g. Menampilkan eror validation hibernite
Branch : showErorHibernite
url : https://github.com/rikialdi/binar_grab.git
a. Strukture
107
apiResponse
package com.binar.grab.controller.validationpass.web;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class ApiResponse {
Handler
package com.binar.grab.controller.validationpass.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse
handleValidationExceptions(MethodArgumentNotValidException ex) {
ex.getBindingResult().getFieldErrors().forEach(error -> {
if (errors.containsKey(error.getField())) {
errors.put(error.getField(), String.format("%s,
%s", errors.get(error.getField()), error.getDefaultMessage()));
} else {
errors.put(error.getField(),
error.getDefaultMessage());
}
}
108
);
return new ApiResponse(errors, "VALIDATION_FAILED");
}
}
b. Controller
Pada setiap controller tambahkan yang dikotak merah
c. Entity
Setiap req atau entity tambahkan yang dikotak merah dibawah ini
109
d. Testing
f. Anotasi hibernite
import javax.validation.constraints.Max;
@Min(2)
@Max(100)
public String username;
110
b. @Size Batasin Request maksimal 5 huruf
import javax.validation.constraints.*;
@Size(
min = 5,
max = 14,
message = "The author email '${validatedValue}' must be between
{min} and {max} characters long"
)
public String email;
111
c. Mengenal Lambda Expression untuk Membuat Fungsi
Anonymous di Java
Ref: https://www.petanikode.com/java-lambda/
112
Oke, kalau begitu.. bagaimana cara membuat fungsi anonymous?
() -> {}
Keterangan:
Contoh:
Sebenarnya simbol yang perlu diingat adalah simbol lambda ( ->), karena simbol ini
yang membedakan fungsi anonymous dengan fungsi biasa.
113
Untuk tanda kurung yang ini () dengan yang ini {}, di fungsi biasa juga ada.
Karena cuma ada satu baris ekspresi, maka tanda kurung {} boleh tidak ada.
int variabel;
int arr;
variabel = () -> { return 0 };
arr = () -> { return {0,4,3,2,1} };
int methodName(){
return () -> { return 0 };
}
() -> {
return () -> 5 + 2;
114
};
String jawab = (int x) -> { x < 10} ? () -> return "yes": () -> retu "no";
Tapi masalahnya:
Saat interface hanya memiliki satu method saja untuk diimplementasikan. Kita harus
membuat class (anonymous) baru.
Di sini lah saat yang tepat untuk menggunakan fungsi anonymous atau lambda
expression.
interface Clickable {
115
void onClick();
}
void doClick(){
action.onClick();
}
}
}
}
116
Perhatikan baris kode yang saya tandai di atas..
}
}
Pada dasarnya, fungsi anonymous adalah bentuk sederhana dari class anonymous.
117
Jadi dia akan memiliki akses variabel yang sama seperti class anonymous.
Kalau tidak percaya, coba buktikan dengan membuat variabel di luar fungsi
anonymous.
}
}
Fungsi anonymous akan bisa mengakses variabel yang berada di dalam class
(variabel global), dan lokal yang ada di method tempat anonymous class digunakan.
f. Akhir Kata…
Intinya:
118
Jika interface itu memiliki lebih dari satu method, maka sebaiknya pakai class
anonymous.
Daftar Pustaka
https://www.it-swarm-id.com/id/authentication/apa-perbedaan-utama-
antara-otentikasi-jwt-dan-oauth/828156483/
https://undebugable.blogspot.com/2020/12/membuat-authentikasi-
berbasis-token.html
https://www.bezkoder.com/spring-boot-jwt-mysql-spring-security-
architecture/
https://www.bezkoder.com/jwt-json-web-token/
119