[Spring Security + JWT] Phần 2 - Tạo JWT và làm cái api login

phần 1 mình đã tạo một số model và tạo thử một api register, ở phần này mình sẽ hướng dẫn các bạn tạo một chuỗi JWT và khi login sẽ chả về cái chuỗi đó.

JWT

  • Ở phần trước mình đã nói qua rồi, nếu các bạn muốn tìm hiểu chi tiết thì có thể google nhé, có nhiều bài viết khá chi tiết về cái này rồi nên mình không copy lại nữa :grin:
  • Cấu tạo của 1 JWT thì nó sẽ có 3 phần: header, payload và signature
    • Header: sẽ chứa loại kiểu token ở đây là JWT và tên thuật toán.
    • Payload: chỗ này là những dữ liệu mà chúng ta muốn lưu
    • Signature: mình google translate thì nó bảo đây là chữ ký các bạn ạ :sweat_smile:

Ở đây mình dùng thư viện nimbus-jose-jwt để tạo ra JWT nhé, người ta tạo ra sẵn rồi mình chỉ dùng thôi cho nhanh :innocent:

Lại code tiếp nào :musical_note::musical_note::musical_note:

UserPrincipal

  • Thực ra là mình có thể dùng thằng User trong org.springframework.security.core.userdetails nhưng trong thằng đó nó không có userId, mà tí mình cần dùng đến thằng này nên mình tạo ra thằng UserPrincipal kế thừ từ UserDetails, còn mấy cái return false kia trong project này mình không dùng đến, nhưng UserDetails có nên mình cứ override lại và để đấy thôi :imp::mask:
@Getter@Setter
public class UserPrincipal implements UserDetails {
    private Long userId;
    private String username;
    private String password;
    private Collection authorities;

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

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

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

    @Override
    public boolean isEnabled() {
        return false;
    }
}
  • Cái Object này có ý nghĩa là tại bất kỳ đâu trong ứng dụng bạn cũng đều có thể lôi UserPrincipal này ra như này
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
    UserPrincipal userPrincipal = (UserPrincipal) principal;
}

JwtUtil

@Component
public class JwtUtil {

    private static Logger logger = LoggerFactory.getLogger(JwtUtil.class);
    private static final String USER = "user";
    private static final String SECRET = "daycaidaynaychinhlachukycuabandungdelorangoaidaynhenguyhiemchetnguoidayhihihi";

    public String generateToken(UserPrincipal user) {
        String token = null;
        try {
            JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
            builder.claim(USER, user);
            builder.expirationTime(generateExpirationDate());
            JWTClaimsSet claimsSet = builder.build();
            SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
            JWSSigner signer = new MACSigner(SECRET.getBytes());
            signedJWT.sign(signer);
            token = signedJWT.serialize();
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return token;
    }

    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + 864000000);
    }

}
  • builder.claim(USER, user) chỗ này là mình nhét cả cái UserPrincipal vào payload này
  • builder.expirationTime(generateExpirationDate()) còn cái này là thời gian tồn tại của token này
  • signedJWT.sign(signer) đoạn này chính là cái chữ ký

    Đấy đại khái là vậy thôi là tạo ra được cái token rồi, qua bước tiếp theo nhé

Tạo cái để lưu token lại

Mội lần người dùng đăng nhập mình sẽ lưu cái token lại và khi đăng xuất sẽ xóa bỏ nó đi.

TokenRepository

public interface TokenRepository extends JpaRepository<Token, Long> {
}

TokenService

public interface TokenService {

    Token createToken(Token token);

}

TokenServiceImpl

@Service
public class TokenServiceImpl implements TokenService {

    @Autowired
    private TokenRepository tokenRepository;

    public Token createToken(Token token){
        return tokenRepository.saveAndFlush(token);
    }
}

Tạo Api login

UserService

thêm

UserPrincipal findByUsername(String username);

UserServiceImpl

    @Override
    public UserPrincipal findByUsername(String username) {
        User user = userRepository.findByUsername(username);
        UserPrincipal userPrincipal = new UserPrincipal();
        if (null != user) {
            Set<String> authorities = new HashSet<>();
            if (null != user.getRoles()) user.getRoles().forEach(r -> {
                authorities.add(r.getRoleKey());
                r.getPermissions().forEach(p -> authorities.add(p.getPermissionKey()));
            });

            userPrincipal.setUserId(user.getId());
            userPrincipal.setUsername(user.getUsername());
            userPrincipal.setPassword(user.getPassword());
            userPrincipal.setAuthorities(authorities);
        }
        return userPrincipal;
    }

UserRepository

thêm User findByUsername(String username);

AuthController

    @Autowired
    private TokenService tokenService;

    @Autowired
    private JwtUtil jwtUtil;
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody User user){
        UserPrincipal userPrincipal = userService.findByUsername(user.getUsername());
        if (null == user || !new BCryptPasswordEncoder().matches(user.getPassword(), userPrincipal.getPassword())) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("tài khoản hoặc mật khẩu không chính xác");
        }
        Token token = new Token();
        token.setToken(jwtUtil.generateToken(userPrincipal));
        token.setTokenExpDate(jwtUtil.generateExpirationDate());
        token.setCreatedBy(userPrincipal.getUserId());
        tokenService.createToken(token);
        return ResponseEntity.ok(token.getToken());
    }

test thử

đây là user và password mình đã insert vào ở phần trước
alt text
kết quả
alt text

Tổng kết

Vậy là mình đã tạo ra được cái chuỗi JWT (mình chuyển sang gọi nó là token nhé :smile:) và tạo api login trả về token rồi. Ở phần sau mình sẽ làm tiếp cách đọc dữ liệu từ token, kiểm tra token, filter, api logout, api test role, api test permission và cách sử dụng annotation createdBy, updateBy. Cảm ơn các bạn đã đọc!
Để xem lại bài viết trước các bạn xem ở đây nhé: https://kipalog.com/posts/Spring-Security---JWT--Phan-1---Chuan-bi-do-nghe-va-tao-thu-mot-api
phần tiếp theo: https://kipalog.com/posts/Spring-Security---JWT--Phan-3---Doc-JWT-va-lam-mot-vai-api-test-permission

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

hunghh.dev

7 bài viết.
6 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
6 1
Restful api Rest (Representational State Transfer) hiểu nôm na nó là một kiểu kiến trúc được dùng để giao tiếp giữa các máy tính, được truyền tải...
hunghh.dev viết 3 tháng trước
6 1
White
5 0
Xin chào các bạn đọc, trong seri này mình dự định sẽ chia sẻ cách dùng spring security để phân quyền trong web service, cách đọc ghi một JWT, rồi t...
hunghh.dev viết 4 tháng trước
5 0
White
3 0
Microservices cái này hiểu theo cách cơ bản là chia module thành cách service nhỏ để thuận tiện cho việc vận hành, nâng cấp, bảo trì,.... Spring cl...
hunghh.dev viết 1 tháng trước
3 0
Bài viết liên quan
White
0 0
Giới thiệu Trong bài hôm nay chúng ta sẽ tìm hiểu cách handle request POST của Spring Boot. Trước đó, bạn nên biết 1. 「Spring Boot 8」Tạo Web He...
https://loda.me viết 1 năm trước
0 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
7 bài viết.
6 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!