- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
本文将介绍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)操作。让我们一步一步地讨论完整的例子。
找到这个例子中使用的软件。
在eclipse中找到项目结构的打印界面。
如果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文件。
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.concretepage</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-demo</name>
<description>Spring Boot Demo Project</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-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: 自动配置数据库连接相关的设置。
在spring boot中,要配置数据库相关属性、hibernate和logging,我们需要使用application.properties
或application.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数据库模式。
数据库模式
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);
我们有两个表users和articles。在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;
}
}
在我们的例子中,我们没有使用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
。
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;
}
}
在我们的例子中,我们正在使用基于头的认证。当我们不使用基于登录页面的认证时,对于任何对应用程序的请求,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");
}
}
现在我们将创建安全配置文件。
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
。
使用JPA EntityManager
找到用于CRUD操作的DAO类。
IArticleDAO.java
package com.concretepage.dao;
import java.util.List;
import com.concretepage.entity.Article;
public interface IArticleDAO {
List<Article> 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<Article> getAllArticles() {
String hql = "FROM Article as atcl ORDER BY atcl.articleId";
return (List<Article>) 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操作创建具有安全方法的服务方法。
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<Article> 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<Article> 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);
}
}
找到拥有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<Article> getArticleById(@PathVariable("id") Integer id) {
Article article = articleService.getArticleById(id);
return new ResponseEntity<Article>(article, HttpStatus.OK);
}
@GetMapping("articles")
public ResponseEntity<List<Article>> getAllArticles() {
List<Article> list = articleService.getAllArticles();
return new ResponseEntity<List<Article>>(list, HttpStatus.OK);
}
@PostMapping("article")
public ResponseEntity<Void> addArticle(@RequestBody Article article, UriComponentsBuilder builder) {
boolean flag = articleService.addArticle(article);
if (flag == false) {
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("/article/{id}").buildAndExpand(article.getArticleId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
@PutMapping("article")
public ResponseEntity<Article> updateArticle(@RequestBody Article article) {
articleService.updateArticle(article);
return new ResponseEntity<Article>(article, HttpStatus.OK);
}
@DeleteMapping("article/{id}")
public ResponseEntity<Void> deleteArticle(@PathVariable("id") Integer id) {
articleService.deleteArticle(id);
return new ResponseEntity<Void>(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}
创建一个带有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
注解。
为了消费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<String> requestEntity = new HttpEntity<String>(headers);
ResponseEntity<Article> 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<String> requestEntity = new HttpEntity<String>(headers);
ResponseEntity<Article[]> 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<Article> requestEntity = new HttpEntity<Article>(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<Article> requestEntity = new HttpEntity<Article>(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<Article> requestEntity = new HttpEntity<Article>(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网络服务。
mvn clean eclipse:eclipse
然后在eclipse中刷新该项目。通过点击运行为->Java应用程序来运行主类MyApplication
。Tomcat服务器将被启动。
mvn spring-boot:run
Tomcat服务器将被启动。
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愉快!
我对编程真的很陌生,并且在理解 RESTful API 的概念时遇到了一些麻烦。我读过 REST 和 RESTful API。我已经查看了 SO 中已经提出的问题,但似乎无法更好地理解该主题。 在我的
我以为我知道REST /“RESTFul”,restfulservices,webservices,SOA和微服务是什么,但是我遇到了许多不同的定义,我得出的结论是这些术语被过度使用,滥用或完全错误定
我有一个列表,其中有一个“人员和组”列。当我使用 REST 查询行时,我会在此列中列出用户 ID。 我发现这篇文章将帮助我将每个 id 转换为标题 http://www.codeproject.com
我想问一些关于 REST 调用的问题。我是 REST 调用的绿色,我想了解什么是 REST 调用以及如何使用 URL 向服务器发送 REST 调用。谁能给我一些基本的教程或链接供我引用? 另外,如果我
很难说出这里问的是什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或言辞激烈,无法以目前的形式合理回答。如需帮助澄清此问题以便可以重新打开,visit the help center . 8年前关闭
如果有一个 REST 资源我想监视来自其他客户端的更改或修改,那么最好(也是最 RESTful)的方法是什么? 我这样做的一个想法是通过提供特定资源来保持连接打开,而不是在资源不(尚)存在时立即返回。
我有一个可以返回大量项目的 RESTful API,我希望能够使用分页样式技术来限制项目数量,这是 RESTful API 中的一个好主意吗? 如果有可能最好通过链接(在这种情况下为 url)或请求正
我仍然处于适应以 REST 方式做事的过程中。 在我的情况下,客户端软件将与 RESTful 服务交互。很少,客户端会上传其整个实体数据库(每个实体序列化为大约 5kb 的 xml 块)。 也许我错了
设计一个路径解析可能有歧义的 REST API 是否被认为是不好的做法?例如: GET /animals/{id} // Returns the animal with the given ID
我知道 REST 并且知道在不使用 session 的情况下创建 RESTful Web 服务,我更了解它,但我不太了解无状态的概念以及使用 REST 如何使您的应用程序可扩展 有人可以解释 REST
我正在尝试找到解决以下问题的最佳方法:我们的应用程序是SaaS,它支持Web登录的SAML。该应用程序还公开了应该在自动化和无人值守的流程中使用的REST API,这意味着没有交互式用户可以键入凭据。
由于 REST 是无状态的,因此传入的每个请求都不知道传入的前一个请求。在这种情况下是否可以使用连接池? 如果要实现连接池,它将像标准数据库连接一样在每个请求时打开连接池并关闭它。 如何实现 REST
得墨忒耳定律(真的应该是得墨忒耳的建议)说你不应该“穿过”一个物体去接触它们的子物体。如果您作为客户需要执行一些重要的操作,大多数情况下您使用的域模型应该支持该操作。 REST 原则上是一个愚蠢的对象
我唯一真正接触到 REST 的想法已经通过 Ruby on Rails 的 RESTful routing .这非常适合我使用 Rails 构建的基于 CRUD 的应用程序,但因此我对 RESTful
有什么好处 http://www.example.com/app/servlet/cat1/cat2/item 网址 超过 http://www.example.com/app/servlet?c
我知道以前有人问过这类问题。我有我的问题的解决方案,我想知道我是否在任何地方破坏了 REST 或 HTTP 主体。 在我的系统中,我有一个名为 member 的资源。支持通常的GET/POST/PUT
我有一个API,可以执行一些批量处理任务。假设它确实为某些资源命名。 我批量传递了7个请求,其中5个更新成功,2个失败。 我的问题是如何应对。使用HTTP时,我无法同时返回成功和错误。 有一个部分成功
我来自 RPC 世界,但目前正在调查使用 REST 是否适合我的项目。至于据我了解 Wikipedia RESTful 服务的基本思想是提供对集合及其各个元素的访问。 在我的情况下,服务器将是一个测量
我想将REST添加到我的挂毯项目中,因此需要知道如何实现它。 有什么更好的方法? 谢谢。 [编辑,从答案中复制:]我必须将GET,PUT,POST和DELETE服务添加到我的挂毯应用程序中。我看到Ta
让 /users/{id}成为 RESTful 服务中的资源 url。 启用基本身份验证,只有经过身份验证的用户才能访问该 url。 示例场景: User_1 & User_2是经过身份验证的用户,用
我是一名优秀的程序员,十分优秀!