1. SPRING BOOT WORKSHOP 2 days productapp with sec jwt cheetsheet
1. SPRING BOOT WORKSHOP 2 days productapp with sec jwt cheetsheet
___________________________
Day 1:
----------
=> Understand Depdendency Injection
=> bean wiring :xml, annoation, java configuraton
=> Spring boot aop
=> spring mvc architecture
=> crud application
day 2:
-------
=> REST basics
=> Spring boot REST application
=> Spring data
=> Spring boot security
=> spring boot profile
=> spring boot actuator
=> spring boot jdbcTemplate
=> sprng boot openfeign
day 3:
----------
=> Spring boot microservice basics
Day 1: session 1:
____________________
Rest
Controller <------------ Service layer <---------- persistance layer <------
SessionFactory
beside that spring DI help to manage dependency of our project and make our project
flexiable
---------- ProductDaoImplHib
|
ProductService <---------------- ProductDao-------ProductDaoImplJdbc
|
---------- ProductDaoImplUtil
public ProductServiceImpl(){
productDao=new ProductDaoImplUtil(); // or ProductDaoImplJdbc()
}
public List<Product>getProducts(){
// business logic
}
}
* BeanFactory:
- light weight container , dont support many featues
- dont use it
BeanFactory applicationContext=new XmlBeanFactory(new
ClassPathResource("demo.xml"));
* ApplicationContext
- more powerful
ApplicationContext applicationContext=
new ClassPathXmlApplicationContext("demo.xml");
ApplicationContext
-ClassPathXmlApplicationContext
-FileSystemXmlApplicationContext
-AnnotationConfigApplicationContext
-XMLWebApplicationContext
consider: we need to find how much time it take to execute code of service layer
and do logging into a log file
public ProductServiceImpl(){
productDao=new ProductDaoImplUtil(); // or ProductDaoImplJdbc()
}
public List<String>getProducts(){
//how much it take to execute and do logging too ....
// business logic
}
}
session 2:
___________
MVC design pattern
=> Auto-Configuration
=> Microservice
spring-boot:run
@Component
public class ApplicationRunnerBean implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
String collectStr =
Arrays.stream(args.getSourceArgs()).collect(Collectors.joining(","));
System.out.println(collectStr);
}
}
https://www.concretepage.com/spring-boot/spring-boot-commandlinerunner-and-
applicationrunner-example#:~:text=The%20difference%20between%20CommandLineRunner
%20and,spring%20ApplicationArguments%20as%20an%20argument.&text=To%20execute%20them
%20in%20an,Order%20annotation%20or%20Ordered%20interface.
Hello world:
-----------
@RestController
public class HelloRestController {
@RequestMapping("/hello")
public String hello(){
return "spring boot";
}
}
application.properties
---------------------------
server.servlet.context-path=/productapp
server.port=8080
eclipse plugin
spring initilizer
spring cli
https://www.journaldev.com/8195/spring-boot-cli-setup-and-helloworld-example
bannner:
________________
spring.banner.location=classpath:banner.txt
https://devops.datenkollektiv.de/banner.txt/index.html
https://docs.spring.io/spring-boot/docs/1.1.2.RELEASE/reference/html/common-
application-properties.html
@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
@Profile ("client_app_profile_name")
public class ClientAppConfiguration {
//it can be left blank
}
Day 2:
Rest
Controller <------------ Service layer <---------- persistance layer <------
SessionFactory
step 1: application.properties
_______________________
server.servlet.context-path=/productapp
server.port=8082
spring.datasource.url=jdbc:mysql://localhost:3306/demoms?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
in case of h2 database :
---------------------
server.port=8090
server.servlet.context-path=/productapp
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Custom H2 Console URL
spring.h2.console.path=/h2
spring.jpa.hibernate.ddl-auto=update
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
spring.jpa.show-sql=true
@Repository
public interface ProductDao extends JpaRepository<Product, Integer>{
public Product findByName(String name);
}
@Service
@Transactional
public class ProductServiceImpl implements ProductService {
@Autowired
public ProductServiceImpl(ProductDao productDao) {
this.productDao = productDao;
}
@Override
public List<Product> findAll() {
return productDao.findAll();
}
@Override
public Product getById(int id) {
return productDao.findById(id)
.orElseThrow(() -> new ProductNotFoundException("product
with id" + id + " is not found"));
}
@Override
public Product addProduct(Product product) {
productDao.save(product);
return product;
}
@Override
public Product updateProduct(int id, Product product) {
Product productToUpdate= getById(id);
productToUpdate.setPrice(product.getPrice());
productDao.save(productToUpdate);
return productToUpdate;
}
@Override
public Product deleteProduct(int id) {
Product productToDelete= getById(id);
productDao.delete(productToDelete);
return productToDelete;
}
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping(path = "products")
public List<Product>findAll(){
return productService.findAll();
}
@GetMapping(path = "products/{id}")
public Product findById(@PathVariable(name = "id") int id){
return productService.getById(id);
}
@PostMapping(path = "products")
public Product addProduct( @RequestBody Product product){
return productService.addProduct(product);
}
@DeleteMapping(path = "products/{id}")
public Product deleteProduct(@PathVariable(name = "id") int id){
return productService.deleteProduct(id);
}
@PutMapping(path = "products/{id}")
public Product updateProduct(@PathVariable(name = "id") int id, @RequestBody
Product product){
return productService.updateProduct(id, product);
}
}
@RestController
public class ProductController {
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping(path = "products")
public ResponseEntity<List<Product>> findAll(){
return
ResponseEntity.status(HttpStatus.OK).body(productService.findAll());
}
@GetMapping(path = "products/{id}")
public ResponseEntity<Product> findById(@PathVariable(name = "id") int id){
return ResponseEntity.ok(productService.getById(id));
}
@PostMapping(path = "products")
public ResponseEntity<Product> addProduct( @RequestBody Product product){
return
ResponseEntity.status(HttpStatus.CREATED).body(productService.addProduct(product));
}
@DeleteMapping(path = "products/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable(name = "id") int id){
productService.deleteProduct(id);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@PutMapping(path = "products/{id}")
public ResponseEntity<Product> updateProduct(@PathVariable(name = "id") int
id, @RequestBody Product product){
return
ResponseEntity.status(HttpStatus.CREATED).body(productService.updateProduct(id,
product));
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErrorDetails {
private String message;
private String details;
@RestControllerAdvice
public class ExHandler {
@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<ErrorDetails> handle404(Exception ex, WebRequest req){
ErrorDetails details=new ErrorDetails();
details.setDate(new Date());
details.setDetails(req.getDescription(true));
details.setName("[email protected]");
details.setDetails(ex.toString());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(details);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDetails> handle500(Exception ex, WebRequest req){
ErrorDetails details=new ErrorDetails();
details.setDate(new Date());
details.setDetails(req.getDescription(true));
details.setName("[email protected]");
details.setDetails(ex.toString());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(details);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDetails> handle500(Exception ex, WebRequest req){
ErrorDetails details=new ErrorDetails();
details.setDate(new Date());
details.setDetails(req.getDescription(true));
details.setName("[email protected]");
details.setDetails(ex.toString());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(details);
}
3.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductDto {
@NotNull(message = "{product.name.absent}")
@Pattern(regexp = "[A-Za-z]+( [A-Za-z]+)*", message =
"{product.name.invalid}")
private String name;
@NotNull(message = "{product.price.absent}")
@Range(min = 100, max = 100000, message = "{product.price.invalid}")
private BigDecimal price;
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String>
handleInvalidArgument(MethodArgumentNotValidException ex) {
ValidationMessages.properties
-----------------------------
product.name.absent=Please provide product name
product.name.invalid=product Name should contain only alphabets and space
@Email(message = "{account.email.invalid}")
@NotNull(message = "{account.email.absent}")
private String email;
@NotNull(message = "{account.phone.absent}")
@Pattern(regexp = "[789][0-9]{9}", message = "{account.phone.invalid}")
private String phone;
EL BOOKS FMCG
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = EmployeeTypeValidator.class)
public @interface ValidateProductType {
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Step 2:
@GetMapping(path = "products", produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- hey maven removed defualt log4j from from my project -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
Disable logging :
---------------
logging.level.root=OFF
logging.level.org.springframework.boot=OFF
spring.main.banner-mode=OFF
Customizing logging :
---------------
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
logging.level.com.productapp=info
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
logging.file.name=/home/raj/Desktop/logs/server.log
@GetMapping(path = "productsV2/{id}")
public EntityModel<Product> findByIdLink(@PathVariable(name = "id") int id){
Link
link=linkTo(methodOn(ProductController.class).findByIdLink(id)).withSelfRel();
Product product=productService.getById(id);
product.add(link);
return EntityModel.of(product);
}
@GetMapping(path = "productsV2")
public CollectionModel<Product> findAllV2(){
List<Product> products=productService.findAll();
for(Product product: products) {
Link
link=linkTo(methodOn(ProductController.class).findByIdLink(product.getId())).withSe
lfRel();
product.add(link);
}
return CollectionModel.of(products);
}
Step 1:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.4</version>
</dependency>
Step 2:
http://localhost:8090/bookapp/v3/api-docs
http://localhost:8090/bookapp/swagger-ui/index.html
http://localhost:8090/bookapp/v3/api-docs.yaml
Step 3:
Customization location
springdoc.swagger-ui.path=/swagger-ui-bookapp.html
10. caching
_____________
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public CacheManager cacheManager(){
ConcurrentMapCacheManager cacheManager=new
ConcurrentMapCacheManager("products");
return cacheManager;
}
}
@Slf4j
@Service
@Transactional
public class ProductServiceImpl implements ProductService {
@Cacheable(value="products")
@Override
public List<Product> findAll() {
//
}
@CachePut(value="products", key="#result.id")
@Override
public Product addProduct(Product product) {
//
}
@CachePut(value="products", key="#result.id")
@Override
public Product updateProduct(int id, Product product) {
//
}
@CacheEvict(value="products", key="#id")
@Override
public Product deleteProduct(int id) {
//
}
@CacheEvict(value="products", allEntries=true)
@Override
public void evictCache() {
log.info("cache is cleared...");
}
}
Step 11: schedule processes
____________________________
Note:
The simple rules that we need to follow to annotate a method with @Scheduled
are:
@Service
public class ScheduledJob {
private Logger logger =
LoggerFactory.getLogger(ScheduledJob.class);
@Autowired
private ProductService service;
logger.info("< cronJob");
}
logger.info("< fixedRateJob");
}
ref:
https://www.baeldung.com/spring-scheduled-tasks
https://www.tutorialspoint.com/unix_commands/crontab.htm
server.port=8080
spring.devtools.restart.enabled=true
#management.endpoints.web.exposure.exclude=*
management.endpoints.web.exposure.include=health, custom-endpoint
management.endpoint.health.show-details=always
management.health.disk.enabled=true
management.health.livenessstate.enabled=true
management.health.readinessstate.enabled=true
management.server.port=9090
@Configuration
@Endpoint(id = "custom-endpoint")
public class CustomEndpoints {
@ReadOperation
public String getCustomData(){
return "This is custom Data";
}
}
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
spring.mvc.view.prefix:/WEB-INF/views/
spring.mvc.view.suffix:.jsp
3. define controller
@Controller
public class ProductController {
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping("products")
public ModelAndView allProducts(ModelAndView mv) {
mv.setViewName("products");
mv.addObject("products", productService.findAll());
return mv;
}
}
<table>
<thead>
<tr>
<th>product id</th>
<th>product name</th>
<th>product price</th>
</tr>
</thead>
<tbody>
<c:forEach items="${products}" var="product">
<tr>
<td>${product.id }</td>
<td>${product.name }</td>
<td>${product.price }</td>
</tr>
</c:forEach>
</tbody>
</table>
RestTemplate tutorial:
______________________
=> RestTemplate is used to communication bw different spring boot application
=> key for microservice project development
=> Microservices communication is possible using RestTemplate
=> RestTemplate various method to intract bw diff microservices
=> methods:
xxxForObject()-getForObject(), postForObject(), getForEntity(),
postForEntity()
xxxForEntity()
exchange()-calling Http POST/PUT/DELETE endpoint
Example:
________
Refer product application
getForObject method:
==================
getting an object :
____________________
Product product=restTemplate.getForObject("http://localhost:8082/productapp/
products/2", Product.class);
getting an Product by id :
_________________________
String productById="http://localhost:8082/productapp/products/{id}";
Map<String, String> prop=new HashMap<>();
prop.put("id", id);
Product product=restTemplate.getForObject(productById,Product.class, prop);
.getForEntity("http://localhost:8082/productapp/products",List.class);
System.out.println(productEntity.getStatusCodeValue());
System.out.println(productEntity.getHeaders());
return productEntity.getBody();
getSingleObject
_________________
Map<String, String> prop=new HashMap<>();
prop.put("id", id);
ResponseEntity<Product> productEntity = restTemplate
.getForEntity("http://localhost:8082/productapp/products/{id}",
Product.class, prop);
return productEntity.getBody();
postForEntity:
______________
ResponseEntity<Product> productEntity = restTemplate
.postForEntity("http://localhost:8082/productapp/products",
product, Product.class);
return productEntity.getBody();
deleteProduct: delete
_________
restTemplate.delete("http://localhost:8082/productapp/products/{id}",
uriVariables);
updateProduct
___________
restTemplate.put("http://localhost:8082/productapp/products/{id}", product,
uriVariables);
System.out.println("updated....");
Feign
___________
=> The Feign is a declarative web service (HTTP client) developed by Netflix.
Its aim is to simplify the HTTP API clients. It is a Java to HTTP client binder.
If you want to use Feign, create an interface, and annotate it.
=> It is a library for creating REST API clients. It makes web service clients
easier.
The developers can use declarative annotations to call the REST
services instead of writing representative boilerplate code.
=> Spring Cloud OpenFeign provides OpenFeign integrations for Spring Boot
apps through auto-configuration and binding to the Spring Environment.
Without Feign, in Spring Boot application, we use RestTemplate to call the User
service.
To use the Feign, we need to add spring-cloud-starter-openfeign dependency in the
pom.xml file.
Step 1:
put openfeign dependency
@EnableFeignClients("com.product.model.service")
@SpringBootApplication
public class ProductApplication {
@FeignClient(name="product-service", url="http://localhost:8082/productapp")
public interface ProductServiceProxy {
@GetMapping(path = "products")
public ResponseEntity<List<Product>> findAll();
@GetMapping(path = "products/{id}")
public ResponseEntity<Product> findById(@PathVariable(name = "id") int id);
@PostMapping(path = "products")
public ResponseEntity<Product> addProduct( @RequestBody Product product);
@DeleteMapping(path = "products/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable(name = "id") int id);
@PutMapping(path = "products/{id}")
public ResponseEntity<Product> updateProduct
(@PathVariable(name = "id") int id, @RequestBody Product product);
@RestController
public class ProductClientController {
@Autowired
private ProductServiceProxy productServiceProxy;
@GetMapping(path = "products")
public ResponseEntity<List<Product>> getAll() {
return productServiceProxy.findAll();
}
@GetMapping(path = "products/{id}")
public ResponseEntity<Product> getById(@PathVariable(name = "id") int id) {
return productServiceProxy.findById(id);
}
@PostMapping(path = "products")
public ResponseEntity<Product> addProduct(@RequestBody Product product) {
return productServiceProxy.addProduct(product);
}
@DeleteMapping(path = "products/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable(name = "id") int id)
{
return productServiceProxy.deleteProduct(id);
}
@FeignClient(name="product-service")
Spring security:
--------------------
step 1: put spring sec dependency
@Component
@EnableWebSecurity//(debug = true)
public class SecConfig extends WebSecurityConfigurerAdapter{
auth.inMemoryAuthentication()
.withUser("raj").password("raj123").roles("ADMIN")
.and()
.withUser("ekta").password("ekta123").roles("MGR");
}
@Bean
public PasswordEncoder encoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/h2/**");
}
@Data
@NoArgsConstructor
@ToString
@Entity
@Table(name = "user_table")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name="user_roles")
private List<String> roles= new ArrayList<>();
@Repository
public interface UserEntityRepo extends JpaRepository<UserEntity, Integer>{
public UserEntity findByUsername(String username);
}
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserEntityRepo entityRepo;
@Override
public UserEntity findByUsername(String username) {
return entityRepo.findByUsername(username);
}
@Override
public void addUserEntity(UserEntity userEntity) {
entityRepo.save(userEntity);
}
@SpringBootApplication
public class ProductappApplication implements CommandLineRunner {
@Autowired
private ProductService productService;
@Autowired
private UserService userService;
@Override
public void run(String... args) throws Exception {
productService.addProduct(new Product("laptop", new
BigDecimal(100000),"raj"));
productService.addProduct(new Product("laptop mouse", new
BigDecimal(1000),"ekta"));
userService.addUserEntity(new UserEntity("raj",
"raj123",Arrays.asList("ROLE_ADMIN","ROLE_MGR")));
userService.addUserEntity(new UserEntity("ekta",
"ekta123",Arrays.asList("ROLE_MGR")) );
System.out.println("------------------------------------------------");
}
}
@Component
public class SecUserDetailService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
UserEntity userEntity= userService.findByUsername(username);
if(userEntity==null)
throw new UsernameNotFoundException("user not found");
//convert UserEntity to the user that can be understand by spring
security
public SecUser() {}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<String> roles= userEntity.getRoles();
String rolesNames[]=roles.toArray(new String[roles.size()]);
return AuthorityUtils.createAuthorityList(rolesNames);
}
@Override
public String getPassword() {
return userEntity.getPassword();
}
@Override
public String getUsername() {
return userEntity.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;
}
@Component
@EnableWebSecurity//(debug = true)
public class SecConfig extends WebSecurityConfigurerAdapter{
@Autowired
private UserDetailsService detailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(detailsService);
}
//...........
@Component
@EnableWebSecurity//(debug = true)
public class SecConfig extends WebSecurityConfigurerAdapter{
//----------
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
//...........
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private PasswordEncoder encoder;
//.................
@Override
public void addUserEntity(UserEntity userEntity) {
userEntity.setPassword(encoder.encode(userEntity.getPassword()));
entityRepo.save(userEntity);
}
//..............
spring sec?
-----------
url pattern
step 1:
step 2:
------------
step 3:
---------
public interface ProductService {
@PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_MGR')")
public List<Product> findAll();
@PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_MGR')")
public Product getById(int id);
//@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public Product addProduct(Product product);
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public Product updateProduct(int id, Product product);
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
//@PostAuthorize("returnObject.vendorName==authentication.name")
//@Secured({"ROLE_ADMIN","ROLE_MGR"})
public Product deleteProduct(int id);
@ExceptionHandler(AccessDeniedException.class)
public final ResponseEntity<ErrorDetails>
handleAccessDeniedException(AccessDeniedException ex) {
ErrorDetails details = new ErrorDetails();
details.setDate(LocalDateTime.now());
details.setName("[email protected]");
details.setDetails(ex.getMessage());
return new ResponseEntity<>(details, HttpStatus.FORBIDDEN);
}
@Component
public class AppAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
@Configuration
public class CorsConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods(GET, POST, PUT, DELETE)
.allowedHeaders("*")
.allowedOriginPatterns("*")
.allowCredentials(true);
}
};
}
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable().authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
Step 1: JwtUtil for util method to create jwt token and to validate
@Component
public class JwtUtil {
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + TOKEN_VALIDITY
* 1000))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JwtRequest {
private String userName;
private String userPassword;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JwtResponse {
private UserEntity user;
private String jwtToken;
}
@Service
public class DetailService implements UserDetailsService {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserService userService;
@Autowired
private AuthenticationManager authenticationManager;
UserEntity userEntity=userService.findByUsername(userName);
return new JwtResponse(userEntity, newGeneratedToken);
}
@Override
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
return secUser;
}
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService jwtService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
response, FilterChain filterChain) throws ServletException, IOException {
if (jwtUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken
usernamePasswordAuthenticationToken = new
UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new
WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthentication
Token);
}
}
filterChain.doFilter(request, response);
@RestController
@CrossOrigin
public class JwtController {
@Autowired
private DetailService jwtService;
@PostMapping({"/authenticate"})
public JwtResponse createJwtToken(@RequestBody JwtRequest jwtRequest) throws
Exception {
return jwtService.createJwtToken(jwtRequest);
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService);
}
// @Autowired
// public void configureGlobal(AuthenticationManagerBuilder
authenticationManagerBuilder) throws Exception {
//
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder
(encode());
// }
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder encode() {
return new BCryptPasswordEncoder();
}
http.addFilterBefore(jwtRequestFilter,
UsernamePasswordAuthenticationFilter.class);
}
}
how to invoke
do post operation:
http://localhost:8090/productapp/authenticate
send
{
"userName": "raj",
"userPassword": "raj123"
}
{
"user": {
"id": 1,
"username": "raj",
"password": "$2a$10$z3GHNjg/jPXgNsftjLvG8ukMcj5GW4nsVyjpXFUCqTERks8XuA.B2",
"roles": [
"ROLE_ADMIN",
"ROLE_MGR"
]
},
"jwtToken":
"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJyYWoiLCJleHAiOjE2NjQ1MzMxNjAsImlhdCI6MTY2NDUxNTE2M
H0.W1-xayM-BH333c5-SiMZFta_uXA6Grz9ov-
8v5TzY7r50WMDzNNG8ka4xOtyylBUxpkuvmTHSQosQ1KPHAcMbw"
}
jwt token:
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJyYWoiLCJleHAiOjE2NjQ1MzMxNjAsImlhdCI6MTY2NDUxNTE2MH
0.W1-xayM-BH333c5-SiMZFta_uXA6Grz9ov-
8v5TzY7r50WMDzNNG8ka4xOtyylBUxpkuvmTHSQosQ1KPHAcMbw
=> externalized
property file should be externalized for loose coupling
=> environment specfic
test, prod, dev, default
=> consistent
In microservice we have many spring boot project that must have same
configuration parameters, we should manage them centerally (using git) rather than
locally
=> version histroy
spring clould provide provision for version history using git
=> real time management
application.properties
____________________
hello.message=jug delhi
#spring.profiles.active=dev
application-dev.properties
@RestController
public class Hello {
@Value("${hello.message}")
private String message;
@GetMapping("/")
public String sayHello() {
return "hello "+ message;
}
}
Providing default value with @Value annotation , so that config not fail at run
time:
_______________________________________________
@RestController
public class Hello {
@GetMapping("/")
public String sayHello() {
return "hello "+ message;
}
}
Using actuator:
______________
Enable actuator:
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
http://localhost:8080/actuator
https://www.javadevjournal.com/spring-boot/spring-boot-actuator/
https://www.javadevjournal.com/spring-boot/spring-boot-actuator-custom-endpoint/
https://medium.com/@ijayakantha/microservices-centralized-configuration-with-
spring-cloud-f2a1f7b78cc2
M1 ---------
M3 -------
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/kr_jdbc?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
commit
step 2: configure config server:
____________________________
spring.application.name=configserver
spring.cloud.config.server.git.uri=/home/raj/Desktop/config
#spring.cloud.config.server.git.clone-on-start=true
server.port=8888
spring.cloud.config.server.git.default-label=master
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
}
http://localhost:8888/application/default
management.endpoints.web.exposure.include=*
spring.config.import=configserver:http://localhost:8888
#spring.application.name=client-app
@RestController
public class Hello {
@GetMapping("/")
public String sayHello() {
return url;
}
}
Step 1:
_______
@RefreshScope
@RestController
public class Hello {
@GetMapping("/")
public String sayHello() {
return url;
}
}
http://localhost:8080/actuator/refresh
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer customerId;
private String emailId;
private String name;
private LocalDate dateOfBirth;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "address_id", unique = true)
private Address address;
}
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer addressId;
private String street;
private String city;
}
@Email(message = "{customer.emailid.invalid}")
@NotNull(message = "{customer.emailid.absent}")
private String emailId;
@NotNull(message = "{customer.name.absent}")
@Pattern(regexp = "[A-Za-z]+( [A-Za-z]+)*", message =
"{customer.name.invalid}")
private String name;
@PastOrPresent(message = "{customer.dob.invalid}")
private LocalDate dateOfBirth;
@NotNull
@Valid
private AddressDTO addressDTO;
@Email(message = "{customer.emailid.invalid}")
@NotNull(message = "{customer.emailid.absent}")
private String emailId;
@NotNull(message = "{customer.name.absent}")
@Pattern(regexp = "[A-Za-z]+( [A-Za-z]+)*", message = "{customer.name.invalid}")
private String name;
@PastOrPresent(message = "{customer.dob.invalid}")
private LocalDate dateOfBirth;
References:
https://jsonplaceholder.typicode.com/users
https://www.javatpoint.com/using-feign-rest-client-for-service-invocation
https://stackoverflow.com/questions/46884362/what-are-the-advantages-and-
disadvantages-of-using-feign-over-resttemplate