Spring Boot Security REST + JPA + Hibernate + MySQL CRUD 示例

x33g5p2x  于2022-09-21 转载在 Spring  
字(25.8k)|赞(0)|评价(0)|浏览(317)

本文将介绍Spring Boot Security REST + JPA + Hibernate + MySQL CRUD示例。当我们使用Spring Boot创建应用程序时,我们只需要写几行代码就可以包含网络、安全和数据库连接等功能。如果Spring Boot在classpath中获得Spring Security,那么它将自动执行与安全有关的配置。同样,如果Spring Boot在其classpath中获得Spring Web和Spring Data,那么Spring Boot会自动执行Spring Web和数据库连接相关的配置。在Spring Security的情况下,Spring Boot默认配置内存认证,使用一个用户和随机密码,在每次服务器重启后都会改变。我们可以在控制台中获得默认密码。在我们的例子中,我们将在数据库中保存用户认证相关的数据,所以我们必须覆盖Spring Boot的默认安全配置。我们将创建一个用@Configuration@EnableWebSecurity注释的安全配置文件。为了覆盖HttpSecurity,我们的安全配置类需要扩展WebSecurityConfigurerAdapter类并覆盖configure()方法。为了启用方法级别的安全,用@EnableGlobalMethodSecurity来注释安全配置类。为了使用数据库对用户进行认证和授权,我们需要实现UserDetailsService接口。我们将在我们的REST web服务例子中处理CREATE、READ、UPDATE和DELETE(CRUD)操作。让我们一步一步地讨论完整的例子。

使用的软件

找到这个例子中使用的软件。

  1. Java 8
  2. Spring Boot 1.5.3.RELEASE
  3. Maven 3.3
  4. MySQL 5.5
  5. Eclipse Mars

Eclipse中的项目结构

在eclipse中找到项目结构的打印界面。

Spring Boot默认认证

如果spring security在classpath中,那么我们的spring boot web应用程序就会自动默认使用基本认证来保证安全。一个默认的用户名 "user "和随机密码将在服务器启动时显示在控制台中,可用于登录认证。密码在控制台显示如下。

Using default security password: 7e9850aa-d985-471a-bae1-25d741d4da23

上述密码是随机的,并且在服务器重启时改变。默认情况下,spring使用内存认证,单用户命名为 "user"。找到一些配置。
**1.**要在spring boot应用程序中启用spring安全,只需在maven或gradle文件中使用以下spring-boot。

spring-boot-starter-security

2. 为了改变默认密码,spring boot提供了security.user.password属性,需要在application.properties中进行配置,如下所示。

security.user.password= concretepage

现在我们可以使用user/concretepage凭证登录应用程序。其他安全属性也可以通过SecurityProperties使用security.*前缀在application.properties中改变,如下所示。

security.basic.enabled。它启用基本认证。默认值是true
security.basic.path: 它配置了应用安全的路径。我们需要提供逗号分隔的路径。
security.enable-csrf: 它启用CSRF。默认值是false
security.require-ssl: 它启用和禁用SSL。默认值为false
security.session: 默认值是无状态。值可以是总是、从不、如果需要、无状态。
security.user.name: 它配置了用户名称。默认的用户是user
security.user.password: 它配置了密码。
security.user.role: 它配置了角色。默认的角色是用户

3. 如果我们已经微调了我们的日志配置,那么为了打印默认的随机密码,我们需要在application.properties中配置以下属性,并设置INFO级别。

logging.level.org.springframework.boot.autoconfigure.security= INFO

**4.**默认的静态路径是不安全的,如/css/**/js/**/images/**/webjars/****/favicon.ico
**5.**在spring security中默认提供HSTS、XSS、CSRF、缓存等功能。

上述属性可以使用security.*来打开或关闭,但如果我们想在数据库中使用用户名和密码,那么我们需要使用UserDetailsService。为了控制安全相关的配置,我们可以创建一个安全配置类,它将扩展WebSecurityConfigurerAdapter,然后覆盖configure()方法。这个类将被注解为@Configuration@EnableWebSecurity。如果我们想启用方法级安全,该类将被注解为@EnableGlobalMethodSecurity

Maven文件

找到本例中使用的maven文件。
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
&ltproject 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	&ltmodelVersion&gt4.0.0</modelVersion>
	&ltgroupId&gtcom.concretepage</groupId>
	&ltartifactId&gtspring-boot-demo</artifactId>
	&ltversion&gt0.0.1-SNAPSHOT</version>
	&ltpackaging&gtjar</packaging>
	&ltname&gtspring-demo</name>
	&ltdescription&gtSpring Boot Demo Project</description>
	&ltparent>
		&ltgroupId&gtorg.springframework.boot</groupId>
		&ltartifactId&gtspring-boot-starter-parent</artifactId>
		&ltversion&gt1.5.3.RELEASE</version>
	</parent>
	&ltproperties>
		&ltjava.version&gt1.8</java.version>
	</properties>
	&ltdependencies>
	    &ltdependency>
		 &ltgroupId&gtorg.springframework.boot</groupId>
		 &ltartifactId&gtspring-boot-starter-web</artifactId>
	    </dependency>
	    &ltdependency>
		 &ltgroupId&gtorg.springframework.boot</groupId>
		 &ltartifactId&gtspring-boot-starter-security</artifactId>
	    </dependency>		
	    &ltdependency>
		 &ltgroupId&gtorg.springframework.boot</groupId>
		 &ltartifactId&gtspring-boot-starter-data-jpa</artifactId>
	    </dependency>	
	    &ltdependency>
		 &ltgroupId&gtmysql</groupId>
		 &ltartifactId&gtmysql-connector-java</artifactId>
	    </dependency>	 
    	    &ltdependency>
                 &ltgroupId&gtorg.springframework.boot</groupId>
                 &ltartifactId&gtspring-boot-devtools</artifactId>
                 &ltoptional&gttrue</optional>
            </dependency> 
	</dependencies> 
	&ltbuild>
	   &ltplugins>
	 	&ltplugin>
	        	&ltgroupId&gtorg.springframework.boot</groupId>
			&ltartifactId&gtspring-boot-maven-plugin</artifactId>
		</plugin>
	   </plugins>
	</build>
</project>

当Spring Boot应用程序在classpath中找到任何JAR时,那么Spring Boot会自动配置所需的设置。
spring-boot-starter-web。自动配置Web应用程序设置。
spring-boot-starter-security。自动配置安全相关的设置。
spring-boot-starter-data-jpa: 自动配置数据库连接相关的设置。

application.properties

在spring boot中,要配置数据库相关属性、hibernate和logging,我们需要使用application.propertiesapplication.yml。这些文件会被Spring Boot自动读取。
application.properties

#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/concretepage
spring.datasource.username=root
spring.datasource.password=
spring.datasource.tomcat.max-wait=20000
spring.datasource.tomcat.max-active=50
spring.datasource.tomcat.max-idle=20
spring.datasource.tomcat.min-idle=15

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.id.new_generator_mappings = false
spring.jpa.properties.hibernate.format_sql = true

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

#Security Configuration---
#security.user.password= concretepage
#prints default password---
#logging.level.org.springframework.boot.autoconfigure.security= INFO

使用spring.datasource.*来配置数据源相关属性。使用spring.jpa.properties.*来配置JPA相关的属性。在我们的例子中,我们正在使用JPA与hibernate。

MySQL数据库模式和Java实体

找到MySQL数据库模式。
数据库模式

CREATE DATABASE IF NOT EXISTS `concretepage` ;
USE `concretepage`;
-- Dumping structure for table concretepage.articles
CREATE TABLE IF NOT EXISTS `articles` (
  `article_id` int(5) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `category` varchar(100) NOT NULL,
  PRIMARY KEY (`article_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
-- Dumping data for table concretepage.articles: ~3 rows (approximately)
INSERT INTO `articles` (`article_id`, `title`, `category`) VALUES
	(1, 'Java Concurrency', 'Java'),
	(2, 'Hibernate HQL ', 'Hibernate'),
	(3, 'Spring MVC with Hibernate', 'Spring');
-- Dumping structure for table concretepage.users
CREATE TABLE IF NOT EXISTS `users` (
  `username` varchar(50) NOT NULL,
  `password` varchar(100) NOT NULL,
  `full_name` varchar(100) NOT NULL,
  `role` varchar(50) NOT NULL,
  `country` varchar(100) NOT NULL,
  `enabled` tinyint(1) NOT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- Dumping data for table concretepage.users: ~2 rows (approximately)
INSERT INTO `users` (`username`, `password`, `full_name`, `role`, `country`, `enabled`) VALUES
	('mukesh', '$2a$10$N0eqNiuikWCy9ETQ1rdau.XEELcyEO7kukkfoiNISk/9F7gw6eB0W', 'Mukesh Sharma', 'ROLE_ADMIN', 'India', 1),
	('tarun', '$2a$10$QifQnP.XqXDW0Lc4hSqEg.GhTqZHoN2Y52/hoWr4I5ePxK7D2Pi8q', 'Tarun Singh', 'ROLE_USER', 'India', 1);

我们有两个表usersarticles。在users表中,我们保存与用户有关的信息,在articles表中,我们保存与文章有关的信息。我们正在使用BCrypt密码编码方案。找到一个简单的主类,可以用来生成BCrypt密码。
Main.java

package com.concretepage;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class Main {
	public static void main(String[] args) {
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		System.out.println(encoder.encode("m123"));
	}
}

对于上述数据库模式中给定的两个表,找到java实体。
UserInfo.java

package com.concretepage.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="users")
public class UserInfo implements Serializable {
	private static final long serialVersionUID = 1L;
	@Id
	@Column(name="username")
	private String userName;
	@Column(name="password")
	private String password;
	@Column(name="role")	
	private String role;
	@Column(name="full_name")	
	private String fullName;
	@Column(name="country")	
	private String country;
	@Column(name="enabled")	
	private short enabled;
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	public String getFullName() {
		return fullName;
	}
	public void setFullName(String fullName) {
		this.fullName = fullName;
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
	public short getEnabled() {
		return enabled;
	}
	public void setEnabled(short enabled) {
		this.enabled = enabled;
	}
}

Article.java

package com.concretepage.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="articles")
public class Article implements Serializable { 
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="article_id")
        private int articleId;  
	@Column(name="title")
        private String title;
	@Column(name="category")	
	private String category;
	public int getArticleId() {
		return articleId;
	}
	public void setArticleId(int articleId) {
		this.articleId = articleId;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getCategory() {
		return category;
	}
	public void setCategory(String category) {
		this.category = category;
	}
}

为用户认证创建DAO

在我们的例子中,我们没有使用Spring Boot默认的内存认证。我们将在MySQL数据库中存储用户认证相关的信息,并使用Hibernate访问它们。因此,我们要创建一个DAO方法来返回给定用户名的用户信息。
IUserInfoDAO.java

package com.concretepage.dao;
import com.concretepage.entity.UserInfo;
public interface IUserInfoDAO {
     UserInfo getActiveUser(String userName);
}

UserInfoDAO.java

package com.concretepage.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.concretepage.entity.UserInfo;
@Repository
@Transactional
public class UserInfoDAO implements IUserInfoDAO {
	@PersistenceContext	
	private EntityManager entityManager;
	public UserInfo getActiveUser(String userName) {
		UserInfo activeUserInfo = new UserInfo();
		short enabled = 1;
		List<?> list = entityManager.createQuery("SELECT u FROM UserInfo u WHERE userName=? and enabled=?")
				.setParameter(1, userName).setParameter(2, enabled).getResultList();
		if(!list.isEmpty()) {
			activeUserInfo = (UserInfo)list.get(0);
		}
		return activeUserInfo;
	}
}

Spring @Transactional注解的作用是使DAO方法具有事务性。我们正在使用JPA API进行数据库事务处理,因此我们将使用依赖注入来实例化EntityManager。为了实现这一点,我们创建了EntityManager属性,并注释了@PersistenceContext

实现UserDetailsService

Spring提供了UserDetailsService来验证和授权用户。它从我们的DAO接收用户相关的数据。
MyAppUserDetailsService.java

package com.concretepage.config;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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 com.concretepage.dao.IUserInfoDAO;
import com.concretepage.entity.UserInfo;
@Service
public class MyAppUserDetailsService implements UserDetailsService {
	@Autowired
	private IUserInfoDAO userInfoDAO;
	@Override
	public UserDetails loadUserByUsername(String userName)
			throws UsernameNotFoundException {
		UserInfo activeUserInfo = userInfoDAO.getActiveUser(userName);
		GrantedAuthority authority = new SimpleGrantedAuthority(activeUserInfo.getRole());
		UserDetails userDetails = (UserDetails)new User(activeUserInfo.getUserName(),
				activeUserInfo.getPassword(), Arrays.asList(authority));
		return userDetails;
	}
}

实现BasicAuthenticationEntryPoint

在我们的例子中,我们正在使用基于头的认证。当我们不使用基于登录页面的认证时,对于任何对应用程序的请求,Spring需要发送一个带有适当状态代码的错误。Spring提供了BasicAuthenticationEntryPoint,需要通过实现它来实现。它有一个方法commence(),我们将覆盖该方法并返回一个状态代码(401),未授权的状态代码包含认证所需的认证类型的头。在我们的例子中,我们使用的是基本认证。
AppAuthenticationEntryPoint.java

package com.concretepage.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class AppAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
		     AuthenticationException authException) throws IOException, ServletException {
		response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\"");
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
	}
	@Override
	public void afterPropertiesSet() throws Exception {
		setRealmName("MY APP REALM");
	}
}

Spring Security REST JavaConfig

现在我们将创建安全配置文件。
SecurityConfig.java

package com.concretepage.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private MyAppUserDetailsService myAppUserDetailsService;	
	@Autowired
	private AppAuthenticationEntryPoint appAuthenticationEntryPoint;
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.csrf().disable()
		    .authorizeRequests()
		    .antMatchers("/user/**").hasAnyRole("ADMIN","USER")
		    .and().httpBasic().realmName("MY APP REALM")
		    .authenticationEntryPoint(appAuthenticationEntryPoint);
	} 
        @Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    	        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
                auth.userDetailsService(myAppUserDetailsService).passwordEncoder(passwordEncoder);
	}
}

该类已被注解为@EnableWebSecurity,它从类WebSecurityConfigurer中配置了spring security。如果我们想覆盖WebSecurityConfigurer的任何方法,那么我们要扩展WebSecurityConfigurerAdapter。在我们的例子中,为了配置HttpSecurity,我们已经覆盖了configure()方法。这里我们授权了一个带有/user/**模式的URL。我们还将在这里配置BasicAuthenticationEntryPoint的实现类。
现在autowire方法configureGlobal(),我们在其中配置了UserDetailsService的实现类和BCryptPasswordEncoder编码方案。
为了保证服务方法的安全,我们需要使用@EnableGlobalMethodSecurity注解。为了使用@Secured注解实现方法级别的安全,配置securedEnabled元数据的值为true。要启用@PreAuthorize@PostAuthorize注解,配置prePostEnabled元数据的值为true

为CRUD操作创建DAO

使用JPA EntityManager找到用于CRUD操作的DAO类。
IArticleDAO.java

package com.concretepage.dao;
import java.util.List;
import com.concretepage.entity.Article;
public interface IArticleDAO {
    List&ltArticle> getAllArticles();
    Article getArticleById(int articleId);
    void addArticle(Article article);
    void updateArticle(Article article);
    void deleteArticle(int articleId);
    boolean articleExists(String title, String category);
}

ArticleDAO.java

package com.concretepage.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.concretepage.entity.Article;
@Transactional
@Repository
public class ArticleDAO implements IArticleDAO {
	@PersistenceContext	
	private EntityManager entityManager;	
	@Override
	public Article getArticleById(int articleId) {
		return entityManager.find(Article.class, articleId);
	}
	@SuppressWarnings("unchecked")
	@Override
	public List&ltArticle> getAllArticles() {
		String hql = "FROM Article as atcl ORDER BY atcl.articleId";
		return (List&ltArticle>) entityManager.createQuery(hql).getResultList();
	}	
	@Override
	public void addArticle(Article article) {
		entityManager.persist(article);
	}
	@Override
	public void updateArticle(Article article) {
		Article artcl = getArticleById(article.getArticleId());
		artcl.setTitle(article.getTitle());
		artcl.setCategory(article.getCategory());
		entityManager.flush();
	}
	@Override
	public void deleteArticle(int articleId) {
		entityManager.remove(getArticleById(articleId));
	}
	@Override
	public boolean articleExists(String title, String category) {
		String hql = "FROM Article as atcl WHERE atcl.title = ? and atcl.category = ?";
		int count = entityManager.createQuery(hql).setParameter(1, title)
		              .setParameter(2, category).getResultList().size();
		return count > 0 ? true : false;
	}
}

用安全的方法为CRUD操作创建服务

现在我们将为CRUD操作创建具有安全方法的服务方法。
IArticleService.java

package com.concretepage.service;
import java.util.List;
import org.springframework.security.access.annotation.Secured;
import com.concretepage.entity.Article;
public interface IArticleService {
     @Secured ({"ROLE_ADMIN", "ROLE_USER"})
     List&ltArticle> getAllArticles();
     @Secured ({"ROLE_ADMIN", "ROLE_USER"})
     Article getArticleById(int articleId);
     @Secured ({"ROLE_ADMIN"})
     boolean addArticle(Article article);
     @Secured ({"ROLE_ADMIN"})
     void updateArticle(Article article);
     @Secured ({"ROLE_ADMIN"})
     void deleteArticle(int articleId);
}

所有的方法都可以由拥有ADMIN角色的用户访问。角色为USER的用户只能访问getAllArticles()getArticleById()服务方法。现在找到实现类。
ArticleService.java

package com.concretepage.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.concretepage.dao.IArticleDAO;
import com.concretepage.entity.Article;
@Service
public class ArticleService implements IArticleService {
	@Autowired
	private IArticleDAO articleDAO;
	@Override
	public Article getArticleById(int articleId) {
		Article obj = articleDAO.getArticleById(articleId);
		return obj;
	}	
	@Override
	public List&ltArticle> getAllArticles(){
		return articleDAO.getAllArticles();
	}
	@Override
	public synchronized boolean addArticle(Article article){
            if (articleDAO.articleExists(article.getTitle(), article.getCategory())) {
    	        return false;
             } else {
    	        articleDAO.addArticle(article);
    	        return true;
             }
	}
	@Override
	public void updateArticle(Article article) {
		articleDAO.updateArticle(article);
	}
	@Override
	public void deleteArticle(int articleId) {
		articleDAO.deleteArticle(articleId);
	}
}

为CRUD操作创建控制器

找到拥有CREATE、READ、UPDATE和DELETE(CRUD)操作方法的控制器类。
ArticleController.java

package com.concretepage.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.util.UriComponentsBuilder;
import com.concretepage.entity.Article;
import com.concretepage.service.IArticleService;
@Controller
@RequestMapping("user")
public class ArticleController {
	@Autowired
	private IArticleService articleService;
	@GetMapping("article/{id}")
	public ResponseEntity&ltArticle> getArticleById(@PathVariable("id") Integer id) {
		Article article = articleService.getArticleById(id);
		return new ResponseEntity&ltArticle>(article, HttpStatus.OK);
	}
	@GetMapping("articles")
	public ResponseEntity&ltList&ltArticle>> getAllArticles() {
		List&ltArticle> list = articleService.getAllArticles();
		return new ResponseEntity&ltList&ltArticle>>(list, HttpStatus.OK);
	}
	@PostMapping("article")
	public ResponseEntity&ltVoid> addArticle(@RequestBody Article article, UriComponentsBuilder builder) {
                boolean flag = articleService.addArticle(article);
                if (flag == false) {
        	    return new ResponseEntity&ltVoid>(HttpStatus.CONFLICT);
                }
                HttpHeaders headers = new HttpHeaders();
                headers.setLocation(builder.path("/article/{id}").buildAndExpand(article.getArticleId()).toUri());
                return new ResponseEntity&ltVoid>(headers, HttpStatus.CREATED);
	}
	@PutMapping("article")
	public ResponseEntity&ltArticle> updateArticle(@RequestBody Article article) {
		articleService.updateArticle(article);
		return new ResponseEntity&ltArticle>(article, HttpStatus.OK);
	}
	@DeleteMapping("article/{id}")
	public ResponseEntity&ltVoid> deleteArticle(@PathVariable("id") Integer id) {
		articleService.deleteArticle(id);
		return new ResponseEntity&ltVoid>(HttpStatus.NO_CONTENT);
	}	
}

从spring 4.3开始,我们有请求映射注解,如
@GetMapping用于HTTP GET方法
@PostMapping用于HTTP POST方法
@PutMapping用于HTTP PUT方法
@DeleteMapping用于HTTP DELETE方法

我们为CRUD操作创建了以下URLS。
1. 创建 :
HTTP方法。POST, URL: /user/article

2. 读取 :
HTTP方法。GET, URL: /user/article/{id}
HTTP方法: GET, URL: /user/articles

3. 更新 :
HTTP方法。PUT, URL: /user/article

4. 删除
HTTP方法。delete, url: /user/article/{id}

使用SpringApplication创建主类

创建一个带有main()方法的类,它将调用SpringApplication.run()来运行应用程序。首先下载所有的JAR依赖项,然后编译项目,再启动嵌入式tomcat服务器。
MyApplication.java

package com.concretepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {  
	public static void main(String[] args) {
		SpringApplication.run(MyApplication.class, args);
        }       
}

我们需要用@SpringBootApplication注解来注释这个类,它相当于@Configuration@EnableAutoConfiguration@ComponentScan注解。

使用RestTemplate创建客户端

为了消费REST网络服务,我们使用RestTemplate。对于认证,我们将在HttpHeaders中传递Base64编码的凭证作为username:password令牌,并进行基本授权。
RestClientUtil.java

package com.concretepage.client;
import java.net.URI;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.concretepage.entity.Article;
public class RestClientUtil {
    private HttpHeaders getHeaders() {
    	String credential="mukesh:m123";
    	//String credential="tarun:t123";
    	String encodedCredential = new String(Base64.encodeBase64(credential.getBytes()));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
     	headers.add("Authorization", "Basic " + encodedCredential);
    	return headers;
    }
    public void getArticleByIdDemo() {
    	HttpHeaders headers = getHeaders();  
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/article/{id}";
        HttpEntity&ltString> requestEntity = new HttpEntity&ltString>(headers);
        ResponseEntity&ltArticle> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Article.class, 1);
        Article article = responseEntity.getBody();
        System.out.println("Id:"+article.getArticleId()+", Title:"+article.getTitle()
                 +", Category:"+article.getCategory());      
    }
    public void getAllArticlesDemo() {
    	HttpHeaders headers = getHeaders();  
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/articles";
        HttpEntity&ltString> requestEntity = new HttpEntity&ltString>(headers);
        ResponseEntity&ltArticle[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Article[].class);
        Article[] articles = responseEntity.getBody();
        for(Article article : articles) {
              System.out.println("Id:"+article.getArticleId()+", Title:"+article.getTitle()
                      +", Category: "+article.getCategory());
        }
    }
    public void addArticleDemo() {
    	HttpHeaders headers = getHeaders();  
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/article";
	Article objArticle = new Article();
	objArticle.setTitle("Spring REST Security using Hibernate");
	objArticle.setCategory("Spring");
        HttpEntity&ltArticle> requestEntity = new HttpEntity&ltArticle>(objArticle, headers);
        URI uri = restTemplate.postForLocation(url, requestEntity);
        System.out.println(uri.getPath());    	
    }
    public void updateArticleDemo() {
    	HttpHeaders headers = getHeaders();  
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/article";
	Article objArticle = new Article();
	objArticle.setArticleId(1);
	objArticle.setTitle("Update:Java Concurrency");
	objArticle.setCategory("Java");
        HttpEntity&ltArticle> requestEntity = new HttpEntity&ltArticle>(objArticle, headers);
        restTemplate.put(url, requestEntity);
    }
    public void deleteArticleDemo() {
    	HttpHeaders headers = getHeaders();  
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/article/{id}";
        HttpEntity&ltArticle> requestEntity = new HttpEntity&ltArticle>(headers);
        restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, Void.class, 4);        
    }
    public static void main(String args[]) {
    	RestClientUtil util = new RestClientUtil();
        //util.getArticleByIdDemo();
        util.getAllArticlesDemo();
    	//util.addArticleDemo();
    	util.updateArticleDemo();
    	//util.deleteArticleDemo();
    }    
}

这里我们正在执行创建、读取、更新和删除(CRUD)操作。我们可以用ADMIN角色的mukesh/m123和USER角色的tarun/t123来测试这个应用程序。

运行应用程序

要运行该应用程序,首先在MySQL中创建表,如例子中给出的。现在我们可以通过以下方式运行REST网络服务。

  1. 使用Eclipse。使用页面末尾的下载链接下载项目的源代码。将该项目导入到eclipse中。使用命令提示符,进入项目的根文件夹并运行。
mvn clean eclipse:eclipse

然后在eclipse中刷新该项目。通过点击运行为->Java应用程序来运行主类MyApplication。Tomcat服务器将被启动。

  1. 使用Maven命令。下载项目的源代码。使用命令提示符进入项目的根文件夹,运行命令。
mvn spring-boot:run

Tomcat服务器将被启动。

  1. 使用可执行JAR。使用命令提示符,进入项目的根文件夹并运行命令。
mvn clean package

我们将在目标文件夹中得到可执行JAR spring-boot-demo-0.0.1-SNAPSHOT.jar。运行这个JAR为

java -jar target/spring-boot-demo-0.0.1-SNAPSHOT.jar

Tomcat服务器将被启动。

现在我们准备测试应用程序。要运行客户端,在eclipse中进入RestClientUtil类,点击运行为->Java应用程序
我们也可以使用Postman用户界面测试应用程序。找到打印屏幕。

祝你学习Spring Boot愉快!

相关文章