Skip to main content

Membuat Authentikasi Berbasis Token pada Spring Boot dengan Spring Security dan JWT

Setelah beberapa kali mencari tutorial tentang otentikasi aplikasi web Spring Boot dengan menggunakan JWT yang mudah dipahami akhirnya saya menemukan artikel berbahasa Inggris tapi sangat mudah dipahami  dan diikuti, artikel tersbut berada disini, dengan judul "Spring Boot Token based Authentication with Spring Security & JWT".

Untuk memudahkan orang-orang yang terbiasa membaca artikel dalam bahasa indonesia (termasuk saya sendiri), artikel ini saya buat dan susun ulang (artikel aslinya tidak tertulis dengan runtut dan dapat membuat pemula bingung dengan berbagai error yang muncul) supaya lebih mudah untuk diikuti dan dapat di gunakan bersama.

Applikasi yang akan kita buat adalah aplikasi web yang setiap endpoint-nya hanya bisa di akses oleh role tertentu.

1. Tools Yang Diperlukan

  • IntelliJ Idea text editor.
  • Spring Assistant Plugin.
  • Postman.
  • PostgreSQL + DBeaver.
  • Min Java 8
  • Spring Boot 2.1.8 (dengan Spring Security, Spring Web, Spring Data JPA).
  • jjwt 0.9.1.
  • Maven 3.6.1.

2. Hasil Akhir

Berikut adalah url atau endpoint yang akan kita buat.
MethodsUrlsActions
POST/api/auth/signupsignup new account
POST/api/auth/signinlogin an account
GET/api/test/allretrieve public content
GET/api/test/useraccess User’s content
GET/api/test/modaccess Moderator’s content
GET/api/test/adminaccess Admin’s content

2.1. Signup

http://localhost:8282/api/auth/signup

User signup sebagai user.

User signup dengan multiple roles, user dan moderator.


2.2. Signin

http://localhost:8282/api/auth/signin

User login dengan menukan username dan password dengan bearer token.


2.3. Public Access (Anonim)

http://localhost:8282/api/test/all

Melakukan akses pada public endpoint (tanpa token).



2.4. User Access Biasa (Authenticated)

http://localhost:8282/api/test/user

Akses tanpa token dan ditolak dengan 401.

Akses di terima dengan menyertakan Bearer token dari hasil signin.


2.5. User Moderator (Authenticated)

http://localhost:8282/api/test/mod

Gambar sama saja dengan 2.4 diatas, hanya beda text response.

3.6. User Admin (Authenticated)

http://localhost:8282/api/test/admin

Gambar sama saja dengan 2.4 diatas, hanya beda text response.

3.7. Data Tersimpan (PostgreSQL)

Database

Table users

Table user_roles
tabel roles

Pastikan data sudah terisi di table roles sebelumnya.
INSERT INTO roles(name) VALUES('ROLE_USER');
INSERT INTO roles(name) VALUES('ROLE_MODERATOR');
INSERT INTO roles(name) VALUES('ROLE_ADMIN');

4. Sekilas Info







4.1 Spring Security

  • WebSecurityConfigurerAdapter merupakan bagian utama dari implementasi Spring security. Dia menyediakan konfigurasi HttpSecurity  untuk mengatur cors, csrf, session management, rules untuk dapat melindungi berbagai sumberdaya/informasi di aplikasi kita. Kita bisa meng-extend dan meng-customize konfigurasi bawaannya yang terdiri dari element-element dibawah ini.
  • UserDetailsService merupakan sebuah interface yang memiliki sebuah method untuk mengambil User berdasarkan username dan mengembalikan objek UserDetails yang mana Spring Security akan menggunaknnya untuk melakukan authentication dan validation.
  • UserDetails berisi informasi yang diperukan seperti: username, password, authorities, untuk digunakan sebagai Authentication object.
  • UsernamePasswordAuthenticationToken untuk mendapatkan username dan password dari request yang dikirim oleh form login , AuthenticationManager akan selanjutnya menggunakan ini untuk meng-authenticate akun yang hendak login.
  • AuthenticationManager memiliki sebuah DaoAuthenticationProvider (dibantu 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.

4.2. Implementasi Pada Tutorial

Kita akan mengatur konfigurasi Spring Security & mengimplementasikan Security Objects sbb.

  • WebSecurityConfig extends WebSecurityConfigurerAdapter
  • UserDetailsServiceImpl implements UserDetailsService
  • UserDetailsImpl implements UserDetails
  • AuthEntryPointJwt implements AuthenticationEntryPoint
  • AuthTokenFilter extends OncePerRequestFilter
  • JwtUtils provides methods for generating, parsing, validating JWT
  • controllers handle signup/login requests & authorized requests.
  • AuthController: @PostMapping(‘/signin’), @PostMapping(‘/signup’)
  • TestController: @GetMapping(‘/api/test/all’), @GetMapping(‘/api/test/[role]’)
  • Repository dengan interfaces yang extend Spring Data JPA JpaRepository untuk interaksi denganDatabase PostgreSQL.
    • UserRepository extends JpaRepository<User, Long>
    • RoleRepository extends JpaRepository<Role, Long>
  • Models terdiri dari dua buah models, satu untuk otentikasi yaitu User & satu untuk otorisasi yaitu Role. Mereka memiliki hubungan many-to-many via tabel user_roles.
    1. User: id, username, email, password, roles
    2. Role: id, name
  • Payloads merupakan classes untuk menampung data Request and Response. 

Untuk memahami lebih lanjut terkait Spring Security  ini silahkan buka link berikut ini:

5. Buat Applikasi Spring Boot

Dengan menggunakan Spring Initialzr atau langsung di editor intelliJ Idea.
Dependency yang diperlukan:
  • Spring Data JPA
  • Spring Web
  • Spring Security
  • jjwt (Tidak tersedia di Spring Initialzr maka tambahkan kemudian)
  • PostgreSQL Driver

5.1. Pom dot XML Saya

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt</artifactId>
		<version>0.9.1</version>
	</dependency>
	<dependency>
		<groupId>org.postgresql</groupId>
		<artifactId>postgresql</artifactId>
		<scope>runtime</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.security</groupId>
		<artifactId>spring-security-test</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>jakarta.validation</groupId>
		<artifactId>jakarta.validation-api</artifactId>
		<version>2.0.2</version>
	</dependency>
</dependencies>

5.2. Struktur Project


5.3. Application Properties

server.port=8282

spring.datasource.url= jdbc:postgresql://localhost:5432/roomeo_db
spring.datasource.username= postgres
spring.datasource.password= postgres

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update

# App Properties
roomeo.app.jwtSecret= 123abc123abc123
roomeo.app.jwtExpirationMs= 86400000

6. Model Entity

Kita akan memiliki 2 model (User dan Role), 1 enum (Erole) dan 3 tabel di database (users, roles dan user_roles).

6.1. ERole.java (Enum)

package id.beanary.roomeo_be.models;

public enum ERole {
    ROLE_USER,
    ROLE_MODERATOR,
    ROLE_ADMIN
}

6.2. User.java (Class)

package id.beanary.roomeo_be.models;

import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(	name = "users",
        uniqueConstraints = {
                @UniqueConstraint(columnNames = "username"),
                @UniqueConstraint(columnNames = "email")
        })
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @Size(max = 20)
    private String username;

    @NotBlank
    @Size(max = 50)
    @Email
    private String email;

    @NotBlank
    @Size(max = 120)
    private String password;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(	name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();

    public User() {
    }

    public User(String username, String email, String password) {
        this.username = username;
        this.email = email;
        this.password = password;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
}

6.3. Role.java (Class)

package id.beanary.roomeo_be.models;

import javax.persistence.*;

@Entity
@Table(name = "roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Enumerated(EnumType.STRING)
    @Column(length = 20)
    private ERole name;

    public Role() {

    }

    public Role(ERole name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public ERole getName() {
        return name;
    }

    public void setName(ERole name) {
        this.name = name;
    }
}

7. Payload

7.1. Request Payload

7.1.1. LoginRequest.java

package id.beanary.roomeo_be.payloads.requests;

public class LoginRequest {
    public String username;
    public String password;

    public LoginRequest(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}


7.1.2. SignupRequest.java

package id.beanary.roomeo_be.payloads.requests;

import java.util.List;
import java.util.Set;

public class SignupRequest {
    public String username;
    public String email;
    public String password;
    private Set<String> roles;

    public SignupRequest(String username, String email, String password, Set<String> roles) {
        this.username = username;
        this.email = email;
        this.password = password;
        this.roles = roles;
    }

    public String getUsername() {
        return username;
    }

    public String getEmail() {
        return email;
    }

    public String getPassword() {
        return password;
    }

    public Set<String> getRoles() {
        return roles;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setRoles(Set<String> roles) {
        this.roles = roles;
    }
}

7.2. Reponse Payload

7.2.1. JwtResponse.java 

package id.beanary.roomeo_be.payloads.responses;

import id.beanary.roomeo_be.models.Role;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class JwtResponse {
    public String token;
    public String type = "Bearer";
    public Long id;
    public String username;
    public String email;
    private List<String> roles;

    public JwtResponse(String token, Long id, String username, String email, List<String> roles) {
        this.token = token;
        this.id = id;
        this.username = username;
        this.email = email;
        this.roles = roles;
    }

    public String getToken() {
        return token;
    }

    public Long getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public String getEmail() {
        return email;
    }

    public List<String> getRoles() {
        return roles;
    }

    public String getType() { return type; }

    public void setToken(String token) { this.token = token; }

    public void setId(Long id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setRoles(List<String> roles) {
        this.roles = roles;
    }

    public void setType(String type) {this.type = type;}
}


7.2.2. MessageResponse.java

package id.beanary.roomeo_be.payloads.responses;

public class MessageResponse {
    public String message;

    public MessageResponse(String message) {
        this.message = message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

8. JPA Repository

8.1. RoleRepository.java

package id.beanary.roomeo_be.repositories;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import id.beanary.roomeo_be.models.ERole;
import id.beanary.roomeo_be.models.Role;

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
    Optional<Role> findByName(ERole name);
}

8.2. UserRepository.java

package id.beanary.roomeo_be.repositories;

import java.util.Optional;

import id.beanary.roomeo_be.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);

    Boolean existsByUsername(String username);

    Boolean existsByEmail(String email);
}

9. Security Service

9.1. UserDetailsImpl.java

package id.beanary.roomeo_be.security.services;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import id.beanary.roomeo_be.models.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.fasterxml.jackson.annotation.JsonIgnore;

public class UserDetailsImpl implements UserDetails {
    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String email;

    @JsonIgnore
    private String password;

    private Collection<? extends GrantedAuthority> authorities;

    public UserDetailsImpl(Long id, String username, String email, String password,
                           Collection<? extends GrantedAuthority> authorities) {
        this.id = id;
        this.username = username;
        this.email = email;
        this.password = password;
        this.authorities = authorities;
    }

    public static UserDetailsImpl build(User user) {
        List<GrantedAuthority> authorities = user.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority(role.getName().name()))
                .collect(Collectors.toList());

        return new UserDetailsImpl(
                user.getId(),
                user.getUsername(),
                user.getEmail(),
                user.getPassword(),
                authorities);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    public Long getId() {
        return id;
    }

    public String getEmail() {
        return email;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        UserDetailsImpl user = (UserDetailsImpl) o;
        return Objects.equals(id, user.id);
    }
}

9.2. UserDetailsService.java (Tidak dipakai)

package id.beanary.roomeo_be.security.services;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

9.3. UserDetailsServiceImpl.java


package id.beanary.roomeo_be.security.services;

import id.beanary.roomeo_be.models.User;
import id.beanary.roomeo_be.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    UserRepository userRepository;

    @Override
    @Transactional
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));

        return UserDetailsImpl.build(user);
    }

}

9.4. JWT Utilities


9.4.1. AuthEntryPointJwt.java

package id.beanary.roomeo_be.security.jwt;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

@Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {

    private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {
        logger.error("Unauthorized error: {}", authException.getMessage());
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
    }

}


9.4.2. AuthTokenFilter.java

package id.beanary.roomeo_be.security.jwt;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import id.beanary.roomeo_be.security.services.UserDetailsServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

public class AuthTokenFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            String jwt = parseJwt(request);
            if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
                String username = jwtUtils.getUserNameFromJwtToken(jwt);

                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception e) {
            logger.error("Cannot set user authentication: {}", e);
        }

        filterChain.doFilter(request, response);
    }

    private String parseJwt(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");

        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
            return headerAuth.substring(7, headerAuth.length());
        }

        return null;
    }
}


9.4.3. JwtUtils.java

package id.beanary.roomeo_be.security.jwt;

import java.util.Date;

import id.beanary.roomeo_be.security.services.UserDetailsImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.*;

@Component
public class JwtUtils {
    private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);

    @Value("${roomeo.app.jwtSecret}")
    private String jwtSecret;

    @Value("${roomeo.app.jwtExpirationMs}")
    private int jwtExpirationMs;

    public String generateJwtToken(Authentication authentication) {

        UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();

        return Jwts.builder()
                .setSubject((userPrincipal.getUsername()))
                .setIssuedAt(new Date())
                .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }

    public String getUserNameFromJwtToken(String token) {
        return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
    }

    public boolean validateJwtToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException e) {
            logger.error("Invalid JWT signature: {}", e.getMessage());
        } catch (MalformedJwtException e) {
            logger.error("Invalid JWT token: {}", e.getMessage());
        } catch (ExpiredJwtException e) {
            logger.error("JWT token is expired: {}", e.getMessage());
        } catch (UnsupportedJwtException e) {
            logger.error("JWT token is unsupported: {}", e.getMessage());
        } catch (IllegalArgumentException e) {
            logger.error("JWT claims string is empty: {}", e.getMessage());
        }

        return false;
    }
}


9.5. Konfigurasi Utama

9.5.1. WebSecurityConfig.java

package id.beanary.roomeo_be.security;

import id.beanary.roomeo_be.security.jwt.AuthEntryPointJwt;
import id.beanary.roomeo_be.security.jwt.AuthTokenFilter;
import id.beanary.roomeo_be.security.services.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        // securedEnabled = true,
        // jsr250Enabled = true,
        prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserDetailsServiceImpl userDetailsService;

    @Autowired
    private AuthEntryPointJwt unauthorizedHandler;

    @Bean
    public AuthTokenFilter authenticationJwtTokenFilter() {
        return new AuthTokenFilter();
    }

    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests().antMatchers("/api/auth/**").permitAll()
                .antMatchers("/api/test/**").permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}


Anda bisa menemukan kode sumber di Github.
Sumber referensi belajar https://bezkoder.com/spring-boot-jwt-authentication/#comment-5990.

Tutorial lain yang layak diikuti.



Comments

Popular posts from this blog

Numpang Kerja Remote dari Bandung Creative Hub

Semalam kemarin (09 Januari 2019) tidak sengaja kami sekeluarga lewat Bandung Digital Hub saat pulang dari Fish Wow Cheeseee  yang di Jl. Lombok. Bandung Digital Hub ini sendiri berlokasi tidak jauh dari dari tempat kami makan tersebut, yaitu berlokasi di Jl. Laswi No.7, Kacapiring, Batununggal, Kota Bandung, Jawa Barat 40271. Berhubung untuk bulan Januari 2019 ini sedang tidak masuk ke kantor maka saya putuskan untuk besoknya (hari ini 09 Januari 2019) nyoba untuk bekerja remote dari Bandung Digital Hub , apalagi istri yang kebetulan follower pak Ridwan Kamil di Instagram juga Facebook dan tampaknya pernah lihat ulasan mengenai tempat ini sehingga tampak antusias supaya saya datang ketempat ini ini dan mencoba bekerja dari gedung creative hub dan coworking yang keren ini.  Tempat Parkir Masalah utama saat kita datang ke coworking space terutama yang berlokasi di Bandung (atau mungkin kota-kota lainnya) adalah lahan parkir, kadang lahan parkir ...

Numpang Kerja Remote dari Bandung Digital Valley

Satu lagi co-working place  gratisan dan keren yang cukup populer dikota Bandung, co-working place yang juga memberikan fasilitas tempat kerja (co-working place) dan fitur-fitur menarik lainnya,  co-working place keren  ini adalah Bandung Digital Valley atau yang sering disingkat BDV . C o-working place  Bandung Digital Valley ini  merupakan bagian dari Telkom , mulai aktif digunakan dari sekitar tahun 2012 lalu .  Tempat ini biasanya menjadi tempat favorit bagi para pengiat startup, freelancer, dan mahasiswa . Gedung BDV Gedung BDV Gedung BDV Co-working space Bandung Digital Valley ini sendiri berlokasi di Menara Bandung Digital Valley, Jl. Gegerkalong Hilir No.47, Sukarasa, Sukasari, Kota Bandung, Jawa Barat, detailnya bisa dilihat di Google map berikut. Pemandangan jalan setelah pintu satpam. Free Co-working Space Membership Untuk mulai menggunakan fasilitas co-working space ini secara gratis maka yang pe...