[Spring Security + JWT] Phần 1 - Chuẩn bị đồ nghề và tạo thử một api

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ạo thử một vài api để test :poop: Đây là lần đầu tiên mình viết một bài trên kipalog, nên có thể có nhiều thiếu sót, trong bài viết có chỗ nào chưa đúng các anh chị em góp ý cho mình với ạ, mình xin cảm ơn!

Công nghệ sử dụng

  • Spring boot: để tạo và phát triển ứng dụng nhanh
  • Spring Web: cái này để mình tạo web app, ở đây gọi là web service và mình dùng tiêu chuẩn restful api.
  • Spring Security: còn cái spring security là để xác thực, phân quyền, mã hóa mật khẩu,...
  • Spring Jpa: để mình tương tác với data.
  • JWT (JSON Web Token): ở đây sẽ là một cái chuỗi loằng ngoằng mà server tạo ra cho client, để client ném lên server, server sẽ đọc thông tin trong đó để xử lý.
  • MySQL: là một hệ quản trị cơ sở dữ liệu mà nguồn mở. tóm lại dữ liệu sẽ được mình lưu vào đây :grimacing:
  • Có thể còn thêm :laughing:

Môi trường:

  • Java 15: cái này mình tiện tay nên cứ phang bản mới nhất thôi :blush: các bạn dùng JAVA 8 trở lên là được rồi
  • MySQL 8

Công cụ:

  • IntelliJ IDEA
  • HeidiSQL
  • Postman

Bắt đầu chiến thôi :trollface:

Thư viện sử dụng

Ở đây mình dùng maven nhé

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>com.nimbusds</groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
            <version>8.8</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>

Tạo database + config application.properties base

  • Mình tạo database với tên demo_springjwt
  • Rồi trong application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/demo_springjwt
spring.datasource.username=demo_springjwt
spring.datasource.password=

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.show-sql=true

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
spring.jpa.properties.hibernate.id.new_generator_mappings=true
spring.jpa.hibernate.ddl-auto=update

Tạo entity

Base entity

@Getter
@Setter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private boolean deleted = false;

    @CreatedDate
    private Date createdAt;

    @LastModifiedDate
    private Date updatedAt;

    private Long createdBy;

    private Long updatedBy;
}
  • Để sử dụng được 2 annotations @CreatedDate@LastModifiedDate, ngoài thêm @EntityListeners(AuditingEntityListener.class) thì ở class Application (class chứa main để run app spring) thêm @EnableJpaAuditing.
  • Còn @Getter@Setter mình dùng của thử viện lombok, nếu ide của bạn báo lỗi thì hãy thêm plugin lombok vào hoặc bạn có thể tạo getter, setter như bình thường.

User

@Entity
@Table(name = "t_user")
@Getter
@Setter
public class User extends BaseEntity {

    private String username;

    private String password;

    @OneToMany(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
    @JoinTable(name = "t_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")})
    private Set<Role> roles = new HashSet<>();

}

Role

@Entity
@Table(name = "t_role")
@Getter
@Setter
public class Role extends BaseEntity {

    private String roleName;

    private String roleKey;

    @OneToMany(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
    @JoinTable(name = "t_role_permission", joinColumns = {@JoinColumn(name = "role_id")}, inverseJoinColumns = {@JoinColumn(name = "permission_id")})
    private Set<Permission> permissions = new HashSet<>();
}

Permission

@Entity
@Table(name = "t_permission")
@Getter
@Setter
public class Permission extends BaseEntity {

    private String permissionName;

    private String permissionKey;

}

Token

@Entity
@Table(name = "t_token")
@Getter
@Setter
public class Token extends BaseEntity {

    @Column(length = 1000)
    private String token;

    private Date tokenExpDate;

}

Chuẩn bị dữ liệu

  • đây là dữ liệu mình chuẩn bị sẵn. Ở đây mình tạo ra 4 permissions(thêm, sửa, xóa, xem user). 2 roles(Supper User là role có full quyền, còn khách không có quyền gì cả). Một user với username là hunghh và password là 123456 có role ADMIN.
INSERT INTO `demo_springjwt`.`t_permission` (`permission_key`, `permission_name`) VALUES
 ('USER_CREATE', 'tạo user'), ('USER_READ','xem user'), ('USER_UPDATE', 'sửa user'), ('USER_DELETE', 'xóa user');
INSERT INTO `demo_springjwt`.`t_role` (`role_key`, `role_name`) VALUES
 ('ADMIN', 'Supper User'), ('CUSTOMER', 'Khách');
INSERT INTO `demo_springjwt`.`t_user` (`username`, `password`) VALUES
 ('hunghh', '$2a$10$VObHxmxycf0.QJNXlwqEc.mFZ.iYkS5V4zAJ0xdIMSoEYoW18O7cu');
INSERT INTO `demo_springjwt`.`t_role_permission` (`role_id`, `permission_id`) VALUES
 (1, 1), (1, 2), (1, 3), (1, 4);
INSERT INTO `demo_springjwt`.`t_user_role` (`user_id`, `role_id`) VALUES
 (1, 1);

Giải thích chút chỗ phân quyền của mình

  • Ở đây ý đồ mình đang là 1 role có thể có nhiều permission và một user có thể có nhiều role khác nhau alt text

Tạo thử api đăng ký

UserRepository

public interface UserRepository extends JpaRepository<User, Long> {
}

UserService

public interface UserService {
    User createUser(User user);
}

UserServiceImpl

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public User createUser(User user) {
        return userRepository.saveAndFlush(user);
    }

}

AuthController

@RestController
public class AuthController {

    @Autowired
    private UserService userService;

    @PostMapping("/register")
    public User register(@RequestBody User user){
        user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
        return userService.createUser(user);
    }
}

Bỏ kiểm tra với register

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests().antMatchers("/register").permitAll();
    }
}

Test thử

alt text
kết quả
alt text
những phần chưa có thông tin ta sẽ xử lý ở những phần sau

Phần tiếp theo

  • Ở phần kế tiếp mình sẽ hướng dẫn cách tạo ra JWT và làm thêm cái API đăng nhập ạ. cảm ơn mọi người đã đọc ạ :sleeping::sleeping::sleeping:

Phần 2: https://kipalog.com/posts/Spring-Security---JWT--Phan-2---Tao-JWT-va-lam-cai-api-login
Phần 3: 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

8 bài viết.
7 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 6 tháng trước
6 1
White
3 0
Ở (Link) 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 ch...
hunghh.dev viết 7 tháng trước
3 0
White
3 0
Ở (Link) mình đã tạo ra token và làm cái api đăng nhập rồi trả về token. ở phần này mình sẽ đọc thông tin từ cái token đó xử lý nó và làm thử cái a...
hunghh.dev viết 7 tháng trước
3 0
Bài viết liên quan
White
22 0
Mysql innodb internal là một chủ đề khá sâu. Bản thân tôi cũng chưa bao quát hết. Bài viết này chỉ cung cấp một cái nhìn sơ lược. Để các bạn khôn...
manhdung viết hơn 5 năm trước
22 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

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


White
{{userFollowed ? 'Following' : 'Follow'}}
8 bài viết.
7 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á!