Việc bảo mật thông tin giúp chúng ta giới hạn quyền truy cập vào các trang web, tài nguyên tĩnh, API hay các thông tin nhạy cảm của khách hàng là rất quan trọng.
Ở cấp độ cơ sở hạ tầng, chúng ta có thể áp dụng một số phương pháp, ví dụ như tường lửa (firewall), proxy server, whitelist IP,… Tuy nhiên, vẫn cần phải xây dựng bảo mật ở cấp độ ứng dụng, đặc biệt với những ứng dụng có logic phân quyền người dùng phức tạp thì chúng ta sẽ sử dụng đến việc phải đăng nhập để có thể bảo mật hơn. Với Spring Security, mọi thứ sẽ đơn giản hơn rất nhiều.
Spring security là gì?
Đầu tiên chúng ta nên tìm hiểu Spring Security là gì. Spring Security một phần của Spring Framework, là framework hỗ trợ lập trình viên triển khai các biện pháp bảo mật ở cấp độ ứng dụng. Cùng với Spring MVC, cả hai là bộ khung toàn diện để phát triển các hệ thống web an toàn và bảo mật cao.
Spring Security hoạt động xoay quanh 2 vấn đề chính là xử lý xác thực và xử lý ủy quyền ở cấp độ Web request cũng như cấp độ method invocation.
Spring Security rất mạnh mẽ và có khả năng tùy biến cao, lập trình viên có thể sử dụng cấu hình có sẵn của framework hoặc tùy chỉnh theo từng bài toán của hệ thống.
Bài viết này sẽ tập trung giới thiệu về Spring Security và các tính năng chính của framework này trong việc xây dựng phát triển ứng dụng.
1. Giới thiệu
Trong bài hôm nay chúng ta sẽ tìm hiểu sự kết hợp giữa Spring Security một phần cực kỳ quan trọng trong các hệ thống bảo mật ngày nay, đó là JWT .
JWT (Json web Token) là một chuỗi mã hóa được gửi kèm trong Header của client request có tác dụng giúp phía server xác thực request người dùng có hợp lệ hay không. Được sử dụng phổ biến trong các hệ thống API ngày nay.

2. Cài đặt
Ở dự án này tôi sử dụng java 8 và maven file pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>base_java</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>base_java</name>
<description>base_java</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</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>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.13</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230227</version>
</dependency>
<dependency>
<groupId>org.glassfish.hk2.external</groupId>
<artifactId>bean-validator</artifactId>
<version>2.4.0-b12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Cấu trúc thư mục của tôi ở dự án này:

2.1 Implement
Ban đầu, chúng ta sẽ tạo ra class User và UserDetails để giao tiếp với Spring Security.Trong bài viết có sử dụng Lombok
2.2 Tạo User
Tạo ra class User tham chiếu với database.
package com.example.base_java.entity;
import com.example.base_java.entity.enumeration.Sex;
import lombok.Data;
import org.hibernate.annotations.Where;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
@Entity
@Data
@Where(clause = "is_deleted = false")
public class User extends BaseEntity {
private String userName;
private String password;
private String roleId;
private String email;
private String firstName;
private String lastName;
private String imageUrl;
@Enumerated(EnumType.STRING)
@Column(length = 20, nullable = false)
private Sex sex;
private String phone;
}
2.3 Tạo UserRepository kế thừa JpaRepository để truy xuất thông tin từ database.
package com.example.base_java.repository;
import com.example.base_java.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, String> {
User findByUserName(String username);
}
2.4 Tham chiếu User với UserDetails
Mặc định Spring Security sử dụng một đối tượng UserDetails để chứa toàn bộ thông tin về người dùng. Vì vậy, chúng ta cần tạo ra một class mới giúp chuyển các thông tin của User thành UserDetails
CustomUserDetails.java
package com.example.base_java.config;
import com.example.base_java.entity.User;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Data
@AllArgsConstructor
public class CustomUserDetails implements UserDetails {
User user;
String role;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority(role));
}
// // bạn cũng có thể đặt mặc định role khi đăng nhập bằng 1 role bất kì
// @Override
// public Collection<? extends GrantedAuthority> getAuthorities() {
// // Mặc định mình sẽ để tất cả là ROLE_USER. Để demo cho đơn giản.
// return Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));
// }
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUserName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Khi người dùng đăng nhập thì Spring Security sẽ cần lấy các thông tin UserDetails hiện có để kiểm tra. Vì vậy, bạn cần tạo ra một class kế thừa lớp UserDetailsService mà Spring Security cung cấp để làm nhiệm vụ này.
UserService.java
package com.example.base_java.config.security;
import com.example.base_java.config.CustomUserDetails;
import com.example.base_java.entity.Role;
import com.example.base_java.entity.User;
import com.example.base_java.repository.RoleRepository;
import com.example.base_java.repository.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 javax.transaction.Transactional;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Override
public UserDetails loadUserByUsername(String username) {
// Kiểm tra xem user có tồn tại trong database không?
User user = userRepository.findByUserName(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
String role = roleRepository.findById(user.getRoleId()).map(Role::getRoleName).orElse("USER"); //mặc định sẽ gán quyền User cho các tài khoản không có role
return new CustomUserDetails(user, role);
}
// JWTAuthenticationFilter sẽ sử dụng hàm này
@Transactional
public UserDetails loadUserById(String id) {
User user = userRepository.findById(id).orElseThrow(
() -> new UsernameNotFoundException("User not found with id : " + id)
);
String role = roleRepository.findById(user.getRoleId()).map(Role::getRoleName).orElse("ROLE_USER"); //mặc định sẽ gán quyền User cho các tài khoản không có role
return new CustomUserDetails(user, role);
}
}2.5 Cấu hình JWT
Sau khi có các thông tin về người dùng, chúng ta cần mã hóa thông tin người dùng thành chuỗi JWT. Tôi sẽ tạo ra một class JwtTokenProvider để làm nhiệm vụ này.
package com.example.base_java.config.jwt;
import com.example.base_java.config.CustomUserDetails;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@Slf4j
public class JwtTokenProvider {
// chữ kí
private final String JWT_SECRET = "hihi";
// thời hạn token
private final long JWT_EXPIRATION = 604800000L;
public String generateToken(CustomUserDetails userDetails) {
// Lấy thông tin user
Date now = new Date();
Date expiryDate = new Date(now.getTime() + JWT_EXPIRATION);
// Tạo chuỗi json web token từ id của user.
return Jwts.builder()
.setSubject(userDetails.getUser().getId())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, JWT_SECRET)
.compact();
}
public String getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(JWT_SECRET)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(authToken);
return true;
} catch (MalformedJwtException ex) {
log.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
log.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
log.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
log.error("JWT claims string is empty.");
}
return false;
}
}
2.6 Cấu hình và phân quyền
Bây giờ, chúng ta bắt đầu cấu hình Spring Security bao gồm việc kích hoạt bằng @EnableWebSecurity.
package com.example.base_java.config.security;
import com.example.base_java.config.jwt.AuthenticationEntryPointJwt;
import com.example.base_java.config.jwt.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.List;
@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter implements WebMvcConfigurer{
public final UserService userService;
@Autowired
private AuthenticationEntryPointJwt unauthorizedHandler;
public SecurityConfiguration(UserService userService) {
this.userService = userService;
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
// Get AuthenticationManager Bean
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
// Password encoder, để Spring Security sử dụng mã hóa mật khẩu người dùng
return new BCryptPasswordEncoder();
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
// Phương thức này dùng để xác thực người dùng (authentication)
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userService) // Cung cấp userservice cho spring security
.passwordEncoder(passwordEncoder()); // cung cấp password encoder
}
// tạo list tất cả những api không cần quyền
public static List<RequestMatcher> PERMIT_ALL_URLS = Arrays.asList(
new AntPathRequestMatcher("/auth/login"),
new AntPathRequestMatcher("/auth/register")
);
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.requestMatchers(PERMIT_ALL_URLS.toArray(new RequestMatcher[]{})).permitAll()
.anyRequest().authenticated() // Tất cả các request khác đều cần phải xác thực mới được truy cập
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Thêm một lớp Filter kiểm tra jwt
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Điểm khác biệt ở đây là sự xuất hiện của JwtAuthenticationFilter. Đây là một lớp Filter do tôi tự tạo ra.
JwtAuthenticationFilter Có nhiệm vụ kiểm tra request của người dùng trước khi nó tới đích. Nó sẽ lấy Header Authorization ra và kiểm tra xem chuỗi JWT người dùng gửi lên có hợp lệ không.
package com.example.base_java.config.jwt;
import com.example.base_java.config.security.UserService;
import lombok.extern.slf4j.Slf4j;
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;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private UserService customUserDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
String userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
if(userDetails != null) {
UsernamePasswordAuthenticationToken
authentication = new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails
.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
} catch (Exception ex) {
log.error("failed on set user authentication", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
// Kiểm tra xem header Authorization có chứa thông tin jwt không
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
2.7 Tạo Controller
Vì phần này chúng ta làm việc với JWT, nên các request sẽ dưới dạng Rest API.
Tôi tạo ra 3 api:
/api/auth/login: Cho phép request mà không cần xác thực./api/auth/hihi: Là một api bất kỳ nào đó, phải xác thực mới lấy được thông tin./api/auth/register: Cho phép tạo một user mặc định
2.8 Tạo thông tin User trong database
Trước hết bạn cần cấu hình cho hibernate kết tới tới h2 database trong file resources/appication.properties
server.port=8686
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:6868/demo
spring.datasource.username=demo
spring.datasource.password=demo
spring.datasource.driver-class-name =com.mysql.jdbc.Driver
spring.main.allow-circular-references: true
server.servlet.context-path=/api
#spring.jpa.show-sql: trueCopy
spring.graphql.graphiql.enabled=true
spring.application.name=hihi
upload.folder.path=src/main/resources/uploads
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
Chúng ta sẽ call api /api/auth/register để tạo ra một tài khoản
- Phần xử lí thông tin tài khoản, ở đây tôi có sử dụng một bảng role để map với từng tài khoản với nhau, nếu role Id không tồn tại thì mặc định role đăng nhập ROLE_USER


3. Chạy thử
Tôi sẽ thực hiện chạy trên postman
3.1 Tạo tài khoản


3.2 Chạy api login và lấy mã token
Chúng ta sẽ sử dụng tài khoản đã được tạo ở api register để đăng nhập

Sau khi login chúng ta sẽ nhận được một mã token và mã token này sẽ được sử dụng để thực hiện các request khác.
3.3 Chạy thử api khi chưa và có token
Khi chạy project lên sau đó chúng ta request thử tới địa chỉ http://localhost:8686/api/auth/hihi mà không xác thực.

Kết quả trả về mã lỗi 403 kèm theo message Access Denied.
- Bây giờ chúng ta sẽ sử dụng đến token sau khi đã login

Khi này chúng ta đã có thể thực hiện request này thành công.
4. Kết luận
Bài viết đã hoàn thành vai trò giới thiệu và tóm tắt những tính năng chính của Spring Security và JWT trong việc phát triển ứng dụng Web.
Với Spring Security, lập trình viên có thể tùy chỉnh bảo mật cho hệ thống của mình theo cách đơn giản nhất, thông qua các cấu hình và annotation. Điều này giúp lập trình viên có thể dễ dàng tiếp cận những khía cạnh bảo mật, rút ngắn thời gian phát triển cũng như chi phí vận hành và bảo trì hệ thống.
Rất cảm ơn mọi người đã dành thời gian đọc bài viết của mình, hi vọng nó có thể giúp ích được cho các bạn, nếu có gì chưa chính xác thì mọi người có thể đóng góp ý kiến và cho mình lời khuyên.
Link git soucre code tham khảo: https://gitlab.com/maypham/base_java
116 comments
Hay quá
rất cảm ơn bạn
100 điểm
hihi
Bài viết rất hay, mong bạn viết thêm những bài viết liên quan đến chủ đề này.
rất cảm ơn bạn hy vọng những kiến thức này có thể giúp ích được cho bạn !!
Mình đã thử và thành công. Mình làm được, bạn cũng làm được.
tôi rất hạnh phúc khi bài viết này đã giúp ích được cho bạn
Thanks for any other informative blog. The place else may I get that type of information written in such a perfect way? Ive a undertaking that I am just now operating on and Ive been at the glance out for such information.
Good shout.
SQ
Nice
Nice
thc gummies for anxiety area 52
sativa gummies area 52
best sativa thc carts area 52
snow caps thca area 52
mood thc gummies area 52
live resin gummies area 52
thc gummies for pain area 52
live rosin gummies area 52
buy pre rolls online area 52
thc microdose gummies area 52
thc oil area 52
live resin area 52
XT
thc gummies for sleep area 52
hybrid weed vaporizer area 52
live resin carts area 52
best indica thc weed pens area 52
thc tinctures area 52
best disposable vaporizers area 52
where to buy thca area 52
infused pre rolls area 52
distillate carts area 52
weed pen area 52
thca diamonds area 52
full spectrum cbd gummies area 52
thc gummies
legal mushroom gummies area 52
thcv gummies area 52
indica gummies area 52
thca gummies area 52
liquid diamonds area 52
weed vape area 52
best thca flower area 52
thca disposable area 52
liquid thc area 52
hybrid gummies area 52
KV
This blog was… how do I say it? Relevant!!
Finally I have found something that helped me. Kudos!
Google Analytics Alternative
An outstanding share! I’ve just forwarded this onto
a colleague who had been doing a little research
on this. And he actually bought me lunch because I discovered it for him…
lol. So allow me to reword this…. Thank YOU for the
meal!! But yeah, thanks for spending time to discuss this issue here on your blog.
Incredible! This blog looks just like my old one!
It’s on a completely different subject but it has pretty much the same page layout and design. Excellent choice
of colors!
Have you ever considered writing an e-book or
guest authoring on other websites? I have a blog based upon on the same topics you discuss and would really like
to have you share some stories/information. I know my audience would enjoy your work.
If you’re even remotely interested, feel free to send me an e mail.
Hi there! I know this is somewhat off topic but I was wondering if you knew where I could locate a captcha plugin for my comment form?
I’m using the same blog platform as yours and I’m having difficulty finding one?
Thanks a lot!
Pretty section of content. I just stumbled upon your website
and in accession capital to assert that I get actually
enjoyed account your blog posts. Any way I will be subscribing
to your feeds and even I achievement you access consistently quickly.
I’m truly enjoying the design and layout of your blog.
It’s a very easy on the eyes which makes it much more pleasant for me to come here and visit more often. Did
you hire out a designer to create your theme?
Superb work!
Hi there to every body, it’s my first go to see of this blog; this web site carries awesome and
actually excellent material in favor of readers.
I loved as much as you’ll receive carried out right here.
The sketch is attractive, your authored subject matter stylish.
nonetheless, you command get bought an edginess over that you wish be delivering the following.
unwell unquestionably come more formerly again as exactly the same nearly very often inside case you shield this increase.
Greate pieces. Keep posting such kind of info on your site.
Im really impressed by your blog.
Hey there, You’ve performed a fantastic job. I will certainly digg it and in my
opinion recommend to my friends. I’m sure they will be benefited
from this web site.
you are truly a excellent webmaster. The site loading velocity is incredible.
It seems that you’re doing any distinctive trick.
In addition, The contents are masterpiece. you have performed a magnificent
activity in this subject!
Its such as you read my mind! You appear to grasp so much about this, such as you
wrote the e book in it or something. I feel that you simply
can do with some % to drive the message house a bit,
however other than that, this is fantastic blog. An excellent read.
I’ll certainly be back.
I was very pleased to discover this site. I need to to
thank you for your time just for this wonderful read!!
I definitely enjoyed every part of it and I have you book marked to look at new information in your site.
Pretty component to content. I just stumbled upon your site and in accession capital to claim that I acquire actually loved account
your weblog posts. Anyway I’ll be subscribing on your augment or even I success
you access constantly rapidly.
Hi there i am kavin, its my first occasion to commenting anyplace, when i read
this paragraph i thought i could also make comment due to this brilliant post.
With havin so much content do you ever run into any problems
of plagorism or copyright infringement? My site has a lot of exclusive content I’ve
either authored myself or outsourced but it seems a lot of it is popping it up
all over the web without my authorization. Do you know any solutions to help stop content from being ripped off?
I’d genuinely appreciate it.
Your style is unique compared to other folks
I have read stuff from. I appreciate you for posting when you’ve got the opportunity,
Guess I’ll just book mark this web site.
This website was… how do you say it? Relevant!!
Finally I’ve found something that helped me. Appreciate
it!
My brother recommended I might like this blog.
He was totally right. This post actually made my day. You can not imagine
simply how much time I had spent for this information! Thanks!
I think the admin of this site is actually working
hard in support of his website, as here every data is quality based material.
Hiya very nice web site!! Guy .. Beautiful .. Amazing .. I’ll
bookmark your site and take the feeds additionally? I’m glad to
find a lot of useful info here in the put up, we want work out more techniques in this regard, thanks for sharing.
. . . . .
Howdy! This article could not be written any better!
Going through this post reminds me of my previous roommate!
He continually kept preaching about this. I
most certainly will forward this article to him.
Fairly certain he’s going to have a very good read. Thank
you for sharing!
Hi, I read your new stuff on a regular basis. Your writing style is witty, keep doing what you’re doing!
of course like your web-site but you need to take a look at the spelling on several of your posts.
Several of them are rife with spelling problems and I to find it very troublesome to tell the truth however I’ll certainly come
back again.
I simply could not go away your site before suggesting
that I extremely loved the standard info a person provide for your guests?
Is going to be back regularly in order to inspect new posts
Hi there! This post couldn’t be written any better!
Reading through this article reminds me of my previous roommate!
He constantly kept preaching about this. I’ll send
this post to him. Pretty sure he will have a very good read.
Many thanks for sharing!
Служба применяет современные препараты, которые не вредят домашним питомцам.
Профессиональная дезодорация от табачного запаха
Dbbet букмекерская контора предлагает и спорт, и казино
dbbet apk
Cat casino бонусы начисляются честно,
условия прозрачные
cat casino зеркало на сегодня
Дрип казино вход занимает не больше минуты
drip casino зеркало
Анлим казино онлайн работает стабильно,
интерфейс понятный, регистрация заняла
пару минут
unlim casino промокод
Криптобосс казино предлагает как слоты, так и настольные игры
cryptoboss
Играю в casino rox уже несколько месяцев и впечатления положительные.
Rox casino рабочее зеркало всегда актуальное, проблем с
доступом не возникало. Ассортимент игр большой.
казино рокс
The explanation of how hospice rules work with
Medicare was very compassionate and informative. Thank you
for tackling such a sensitive topic.
Julie O’Hair
Good post. I learn something new and challenging on blogs I stumbleupon on a
daily basis. It’s always interesting to read through content from other authors and practice a little something from other web sites.
Title race updates, league standings and live scores for championship battles
Решил попробовать enomo casino играть онлайн
и остался доволен. Бонусы и
фриспины начислили сразу после регистрации.
Hey there! I’ve been reading your blog for a while now and finally got the bravery to go ahead and give you a shout out
from Houston Texas! Just wanted to say keep up the good job!
I ⅾo not even know how I ended up here, but I thought thiѕ post was ցreat.
I d᧐n’t know who yоu are but certainly
you’re going to a famous bⅼogger if you are not already 😉 Cheers!
My web page :: trading platform
Hmm is anyone else experiencing problems with the pictures on this blog loading?
I’m trying to find out if its a problem on my end or if it’s the blog.
Any feedback would be greatly appreciated.
It’s amazing to go to see this site and reading the views of
all mates regarding this piece of writing, while I am also zealous of getting know-how.
I believe everything wrote was actually very reasonable. However,
what about this? what if you were to write a killer headline?
I ain’t suggesting your information isn’t good, however what
if you added a headline that grabbed people’s attention? I mean Giới
thiệu Spring Security + JWT (Json Web Token) +
Hibernate + Java 8 Example – Tomoshare is kinda boring.
You might glance at Yahoo’s front page and see how they create news headlines to grab viewers
to click. You might add a video or a related picture or two to get readers interested about everything’ve got to say.
In my opinion, it would bring your posts a little livelier.
Зеркало на сегодня помогло зайти в аккаунт без VPN.
Всё прошло быстро и без ошибок.
play fortuna вход
Attractive section of content. I just stumbled upon your website
and in accession capital to assert that I get in fact
enjoyed account your blog posts. Anyway I will be subscribing to your feeds and even I achievement you access consistently quickly.
Please let me know if you’re looking for a writer for your blog.
You have some really great articles and I feel I would be a good asset.
If you ever want to take some of the load off, I’d
absolutely love to write some content for your blog in exchange
for a link back to mine. Please blast me an email if interested.
Regards!
I do consider all of the ideas you have introduced
to your post. They’re really convincing and will
certainly work. Still, the posts are very short for starters.
May you please prolong them a bit from next time?
Thanks for the post.
Great article. I’m going through many of these issues as well..
Truly no matter if someone doesn’t understand after that its up to other visitors
that they will assist, so here it occurs.
I every time used to read paragraph in news papers but now as I am a user of internet so from now I am using net for articles or reviews, thanks to web.
You have made some good points there. I checked on the net to learn more about the
issue and found most individuals will go along with your views on this web site.
Right here is the right webpage for anybody
who really wants to understand this topic. You realize a whole lot its almost hard to argue with you (not that I personally will need to…HaHa).
You definitely put a fresh spin on a subject that’s been written about for years.
Wonderful stuff, just great!
It’s actually a great and helpful piece of info. I am happy that you just shared this helpful info with us.
Please stay us up to date like this. Thanks for sharing.
I’m amazed, I have to admit. Rarely do I come across a blog that’s both educative and entertaining,
and without a doubt, you have hit the nail on the
head. The problem is something that too few folks are speaking intelligently about.
I am very happy that I stumbled across this during my search for something
relating to this.
It is the best time to make a few plans for the longer term
and it is time to be happy. I have learn this post and if I may just I want to counsel you few attention-grabbing issues or
suggestions. Maybe you can write next articles regarding this article.
I wish to read even more things about it!
What’s up all, here every one is sharing such know-how, therefore
it’s nice to read this web site, and I used to pay a quick visit this web site daily.
You actually make it seem so easy with your presentation but I find this
topic to be really something which I think I would never understand.
It seems too complex and extremely broad for me. I am looking forward for your next post, I’ll try to
get the hang of it!
fantastic issues altogether, you just received
a brand new reader. What may you recommend about your submit that
you just made a few days in the past? Any positive?
Hi there I am so excited I found your webpage, I really found you by
error, while I was browsing on Askjeeve for something else,
Regardless I am here now and would just like to say thanks for a remarkable post and a all round interesting blog (I also love the
theme/design), I don’t have time to look over it all
at the moment but I have saved it and also included your
RSS feeds, so when I have time I will be back to read a lot more, Please
do keep up the superb jo.
I am really thankful to the holder of this website who has shared this enormous paragraph
at at this place.
The Casinoly Greece website is available in Greek
and focuses on the theme of Ancient Rome with carefully crafted graphics
and symbols from the historical era.
Hi there I am so grateful I found your web site, I really found
you by error, while I was looking on Aol for something else, Anyways
I am here now and would just like to say many thanks for a fantastic post and a all
round enjoyable blog (I also love the theme/design), I don’t
have time to look over it all at the minute but I have bookmarked it and
also added in your RSS feeds, so when I have time I will be back to read a
great deal more, Please do keep up the awesome job.
Essayez le meilleur nouveau casino en ligne
France Bonus de bienvenue large choix de jeux depots securises et retraits rapides Inscrivez vous
des maintenant
Casino zonder CRUKS biedt je direct toegang tot spannende
spellen zonder gedoe. Geen verificatie, geen limieten, gewoon spelen.
Ideaal voor ervaren spelers die controle in eigen handen willen houden.
ifvod平台,专为海外华人设计,提供高清视频和直播服务。
Азино 777 вход официальное зеркало — работает, выиграл
10к, вывел без проблем.
азино777 официальный сайт зеркало