本文翻譯自:
https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/
的前半部分榆浓,也就是關(guān)于在spring boot中使用jwt的部分
我會(huì)在之后把自己的程序上傳,以及在寫一篇關(guān)于這個(gè)的解釋,但是首先我們要把原文看一遍胡诗。
譯文:
在本博客文章中, 我們將學(xué)習(xí)如何處理使用 Spring boot編寫的 rest api 的身份驗(yàn)證和授權(quán)居暖。我們將從 GitHub 中克隆一個(gè)簡(jiǎn)單的 spring boot應(yīng)用程序, 它公開(kāi)了公共端點(diǎn), 然后我們將使用 spring security和 JWT 來(lái)保護(hù)這些端點(diǎn)。
使用JWT來(lái)保衛(wèi)RESTful API的安全(譯者注:JWTs是JWT的復(fù)數(shù))
JSON Web token(通常稱為 JWT) 是用于對(duì)應(yīng)用程序上的用戶進(jìn)行身份驗(yàn)證的標(biāo)記菱蔬。這一技術(shù)在過(guò)去幾年中獲得了普及, 因?yàn)樗购笈_(tái)能夠通過(guò)驗(yàn)證這些 JWTS 的內(nèi)容來(lái)接受請(qǐng)求。也就是說(shuō), 使用 JWTS 的應(yīng)用程序不再需要保存有關(guān)其用戶的 cookie 或其他session數(shù)據(jù)荒辕。此特性便于可伸縮性, 同時(shí)保證應(yīng)用程序的安全汗销。
在身份驗(yàn)證過(guò)程中, 當(dāng)用戶使用其憑據(jù)成功登錄時(shí), 將返回 JSON Web token, 并且必須在本地保存 (通常在本地存儲(chǔ)中)。每當(dāng)用戶要訪問(wèn)受保護(hù)的路由或資源 (端點(diǎn)) 時(shí), 用戶代理(user agent)必須連同請(qǐng)求一起發(fā)送 JWT, 通常在授權(quán)標(biāo)頭中使用Bearer schema抵窒。
當(dāng)后端服務(wù)器接收到帶有 JWT 的請(qǐng)求時(shí), 首先要做的是驗(yàn)證token弛针。這由一系列步驟組成, 如果其中任何一個(gè)失敗, 則必須拒絕該請(qǐng)求。以下列表顯示了所需的驗(yàn)證步驟:
- 檢查 JWT 格式
- 檢查簽名
- 驗(yàn)證標(biāo)準(zhǔn)請(qǐng)求
- 檢查客戶端權(quán)限 (范圍)
在本文中, 我們不會(huì)深入討論 JWTS 的具體細(xì)節(jié), 但是, 如果需要, 這里提供了更多關(guān)于JWTS的東西 和JWT認(rèn)證.
RESTful Spring Boot API
我們要保護(hù)的restful Spring boot API 是一個(gè)任務(wù)列表管理器李皇。任務(wù)列表是全局保留的, 這意味著所有用戶都將看到并與同一列表進(jìn)行交互削茁。要克隆并運(yùn)行此應(yīng)用程序, 讓我們使用以下命令(譯者注:此處使用到了git):
# clone the starter project
git clone https://github.com/auth0-blog/spring-boot-auth.git
cd spring-boot-auth
# run the unsecured RESTful API
gradle bootRun
如果一切正常工作, 我們r(jià)estful spring boot API 將啟動(dòng)和運(yùn)行。為了測(cè)試它, 我們可以使用像Postman 或者curl這樣的工具來(lái)向可用端點(diǎn)發(fā)出請(qǐng)求(譯者注:原文作者這里用的是curl):
# issue a GET request to see the (empty) list of tasks
curl http://localhost:8080/tasks
# issue a POST request to create a new task
curl -H "Content-Type: application/json" -X POST -d '{
"description": "Buy some milk(shake)"
}' http://localhost:8080/tasks
# issue a PUT request to update the recently created task
curl -H "Content-Type: application/json" -X PUT -d '{
"description": "Buy some milk"
}' http://localhost:8080/tasks/1
# issue a DELETE request to remove the existing task
curl -X DELETE http://localhost:8080/tasks/1
上面的命令中使用的所有端點(diǎn)都在 TaskController 類中定義, 屬于 com.auth0.samples.authapi.task包掉房。除此類外, 此包還包含另外兩個(gè)類:
- Task: 表示應(yīng)用程序中的任務(wù)的實(shí)體模型茧跋。
- TaskRepository: 負(fù)責(zé)處理任務(wù)持久性的類。
我們的應(yīng)用程序的持久性層由一個(gè)名為 HSQLDB 的內(nèi)存中數(shù)據(jù)庫(kù)支持卓囚。我們通常會(huì)在實(shí)際的應(yīng)用程序中使用像 PostgreSQL 或 MySQL 這樣的生產(chǎn)數(shù)據(jù)庫(kù), 但是對(duì)于本教程, 這個(gè)內(nèi)存中的數(shù)據(jù)庫(kù)將足夠使用
在Spring Boot APIs上啟用用戶注冊(cè)
現(xiàn)在, 我們看了一下我們r(jià)estful Spring boot API 暴露的端點(diǎn), 我們將開(kāi)始保護(hù)它瘾杭。第一步是允許新用戶注冊(cè)自己。我們將在此功能中創(chuàng)建的類將屬于一個(gè)叫做 com.auth0.samples.authapi.user的新包哪亿。讓我們創(chuàng)建這個(gè)包并添加一個(gè)名為 ApplicationUser 的新實(shí)體類
package com.auth0.samples.authapi.user;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class ApplicationUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
private String password;
public long getId() {
return id;
}
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;
}
}
這個(gè)實(shí)體類包含三個(gè)屬性:
- id 作為應(yīng)用程序中用戶實(shí)例的主要標(biāo)識(shí)符
- username 用戶用來(lái)識(shí)別自己的用戶名
- password 用于檢查用戶標(biāo)識(shí)的密碼
要管理此實(shí)體的持久性層, 我們將創(chuàng)建一個(gè)稱為 ApplicationUserRepository 的接口粥烁。此接口是 JpaRepository 的擴(kuò)展, 它使我們能夠訪問(wèn)一些常用的方法 (如 save), 并將在 ApplicationUser 類的同一包中創(chuàng)建:
package com.auth0.samples.authapi.user;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ApplicationUserRepository extends JpaRepository<ApplicationUser, Long> {
ApplicationUser findByUsername(String username);
}
我們還在這個(gè)接口上添加了一個(gè)稱為 findByUsername 的方法。當(dāng)我們實(shí)現(xiàn)身份驗(yàn)證功能時(shí), 將使用此方法蝇棉。
允許新用戶注冊(cè)的端點(diǎn)將由新的 @Controller 類處理讨阻。我們將調(diào)用控制器 UserController 并將其添加到與 ApplicationUser 類相同的包中:
package com.auth0.samples.authapi.user;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users")
public class UserController {
private ApplicationUserRepository applicationUserRepository;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public UserController(ApplicationUserRepository applicationUserRepository,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.applicationUserRepository = applicationUserRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@PostMapping("/sign-up")
public void signUp(@RequestBody ApplicationUser user) {
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
applicationUserRepository.save(user);
}
}
端點(diǎn)的實(shí)現(xiàn)非常簡(jiǎn)單。它所做的只是對(duì)新用戶的密碼進(jìn)行加密 (以純文本形式保存并不是一個(gè)好主意), 然后將其保存到數(shù)據(jù)庫(kù)中篡殷。加密過(guò)程由 BCryptPasswordEncoder 的實(shí)例處理, 它是屬于 Spring 安全框架的類钝吮。
現(xiàn)在我們的應(yīng)用程序有兩個(gè)空白:
- 我們沒(méi)有將 Spring 安全框架添加到項(xiàng)目的依賴。
- 沒(méi)有可在 UserController 類中插入的 BCryptPasswordEncoder 的默認(rèn)實(shí)例。
我們通過(guò)將 Spring 安全框架的依賴項(xiàng)添加到. gradle 文件中來(lái)解決第一個(gè)問(wèn)題:奇瘦、
dependencies {
...
compile("org.springframework.boot:spring-boot-starter-security")
}
第二個(gè)問(wèn)題, 缺少的 BCryptPasswordEncoder 實(shí)例, 我們通過(guò)實(shí)現(xiàn)一個(gè)生成 BCryptPasswordEncoder 實(shí)例的方法來(lái)解決棘催。此方法必須用 @Bean 批注, 我們將在Application類中添加它:
package com.auth0.samples.authapi;
// ... other imports
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@SpringBootApplication
public class Application {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
// ... main method definition
}
這就結(jié)束了用戶注冊(cè)功能, 但我們?nèi)匀蝗狈?duì)用戶身份驗(yàn)證和授權(quán)的支持。接下來(lái)我們來(lái)處理這些功能链患。
spring boot的用戶身份驗(yàn)證和授權(quán)
為了支持我們的應(yīng)用程序中的身份驗(yàn)證和授權(quán), 我們將:
- 實(shí)現(xiàn)身份驗(yàn)證filter, 以便向發(fā)送憑據(jù)的用戶發(fā)出 JWTS
- 實(shí)現(xiàn)授權(quán)filter以驗(yàn)證包含 JWTS 的請(qǐng)求
- 創(chuàng)建 UserDetailsService 的自定義實(shí)現(xiàn), 以幫助 Spring security在框架中加載用戶特定的數(shù)據(jù)
- 擴(kuò)展 WebSecurityConfigurerAdapter 類, 以根據(jù)需要自定義安全框架
在繼續(xù)開(kāi)發(fā)這些filter和類之前, 讓我們創(chuàng)建一個(gè)名為 com.auth0.samples.authapi.security 的新包巧鸭。這個(gè)包將保存所有與我們的應(yīng)用程序的安全性相關(guān)的代碼。
身份驗(yàn)證filter
我們要?jiǎng)?chuàng)建的第一個(gè)元素是負(fù)責(zé)身份驗(yàn)證過(guò)程的類麻捻。我們將調(diào)用此類 JWTAuthenticationFilter, 我們將使用以下代碼實(shí)現(xiàn)它:
package com.auth0.samples.authapi.security;
import com.auth0.samples.authapi.user.ApplicationUser;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import static com.auth0.samples.authapi.security.SecurityConstants.EXPIRATION_TIME;
import static com.auth0.samples.authapi.security.SecurityConstants.HEADER_STRING;
import static com.auth0.samples.authapi.security.SecurityConstants.SECRET;
import static com.auth0.samples.authapi.security.SecurityConstants.TOKEN_PREFIX;
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
ApplicationUser creds = new ObjectMapper()
.readValue(req.getInputStream(), ApplicationUser.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = Jwts.builder()
.setSubject(((User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
請(qǐng)注意, 我們創(chuàng)建的身份驗(yàn)證filter擴(kuò)展了 UsernamePasswordAuthenticationFilter 類纲仍。當(dāng)我們?cè)?Spring security中添加新的filter時(shí), 我們可以明確地定義它filter鏈中的位置, 也可以讓框架自行計(jì)算出來(lái)。通過(guò)擴(kuò)展安全框架中提供的篩選器, Spring 可以自動(dòng)確定將其放在安全鏈中的最佳位置贸毕。
我們的自定義身份驗(yàn)證filter覆蓋基類的兩種方法:
- attemptAuthentication: 我們?cè)谶@里分析用戶的憑據(jù)并將其發(fā)給 AuthenticationManager
- successfulAuthentication: 這是用戶成功登錄時(shí)調(diào)用的方法郑叠。我們使用此方法為該用戶生成 JWT。
我們的 IDE 可能會(huì)抱怨此類中的代碼, 原因有兩個(gè)明棍。首先, 代碼從我們尚未創(chuàng)建的類SecurityConstants中導(dǎo)入了四個(gè)常量乡革。第二, 因?yàn)榇祟愒诿麨?JWTS 的類的幫助下生成 JWTS, 它屬于我們沒(méi)有添加到項(xiàng)目的依賴項(xiàng)的庫(kù)。
讓我們先解決缺少的依賴關(guān)系摊腋。在build. gradle 文件中, 讓我們添加以下代碼行:
dependencies {
...
compile("io.jsonwebtoken:jjwt:0.7.0")
}
這將向我們的項(xiàng)目中添加 java JWT: 用于 java 和 Android 庫(kù)的 JSON Web token, 并將解決丟失的類的問(wèn)題》邪妫現(xiàn)在, 我們必須創(chuàng)建 SecurityConstants 類:
package com.auth0.samples.authapi.security;
public class SecurityConstants {
public static final String SECRET = "SecretKeyToGenJWTs";
public static final long EXPIRATION_TIME = 864_000_000; // 10 days
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
public static final String SIGN_UP_URL = "/users/sign-up";
}
此類包含由 JWTAuthenticationFilter 類引用的所有四個(gè)常量, 以及稍后將使用的 SIGN_UP_URL 常量。
授權(quán) Filter
由于我們已經(jīng)實(shí)現(xiàn)了負(fù)責(zé)對(duì)用戶進(jìn)行身份驗(yàn)證的filter, 因此我們現(xiàn)在需要實(shí)現(xiàn)負(fù)責(zé)用戶授權(quán)的filter兴蒸。我們將此filter作為一個(gè)新類 (稱為 JWTAuthorizationFilter) 在 com.auth0.samples.authapi.security 包中創(chuàng)建:
package com.auth0.samples.authapi.security;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import static com.auth0.samples.authapi.security.SecurityConstants.HEADER_STRING;
import static com.auth0.samples.authapi.security.SecurityConstants.SECRET;
import static com.auth0.samples.authapi.security.SecurityConstants.TOKEN_PREFIX;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
我們已經(jīng)擴(kuò)展了 BasicAuthenticationFilter, 使spring在filter鏈中替換為我們的自定義實(shí)現(xiàn)视粮。我們已經(jīng)實(shí)現(xiàn)的filter中最重要的部分是私有 的getAuthentication 方法。此方法從Authorization 標(biāo)頭中讀取 JWT, 然后使用 Jwts 驗(yàn)證令牌橙凳。如果一切就緒, 我們會(huì)在 SecurityContext 中設(shè)置用戶, 并允許請(qǐng)求繼續(xù)進(jìn)行蕾殴。
在spring boot中集成Security Filters
現(xiàn)在, 我們已經(jīng)正確創(chuàng)建了兩個(gè)安全filter, 我們必須在 Spring Security filter 鏈上配置它們。為此, 我們將在 com.auth0.samples.authapi.security 包中創(chuàng)建一個(gè)名為 WebSecurity 的新類:
package com.auth0.samples.authapi.security;
import org.springframework.http.HttpMethod;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.context.annotation.Bean;
import static com.auth0.samples.authapi.security.SecurityConstants.SIGN_UP_URL;
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
我們用 @EnableWebSecurity 對(duì)此類進(jìn)行了注釋, 并使其擴(kuò)展 WebSecurityConfigurerAdapter 以利用 Spring security提供的默認(rèn) web 安全配置岛啸。這使我們可以通過(guò)定義三個(gè)方法來(lái)微調(diào)框架以滿足我們的需要:
- configure(HttpSecurity http): 一種我們可以定義哪些資源是公共的, 哪些是受保護(hù)的的方法钓觉。在我們的例子中, 我們將 SIGN_UP_URL 端點(diǎn)設(shè)置為公共的, 其他所有內(nèi)容都是受保護(hù)的。我們還通過(guò) http.cors() 配置 CORS (跨源資源共享Cross-Origin Resource Sharing) 支持, 并在 Spring Security filter 鏈中添加自定義安全filter坚踩。
- configure(AuthenticationManagerBuilder auth): 一種我們定義了 UserDetailsService 的自定義實(shí)現(xiàn)的方法, 以便在安全框架中加載用戶特定的數(shù)據(jù)荡灾。我們還使用此方法來(lái)設(shè)置應(yīng)用程序使用的加密方法 (BCryptPasswordEncoder)。
- corsConfigurationSource (): 一種可以允許/限制我們的 CORS 支持的方法瞬铸。在我們的情況下, 我們把它公開(kāi), 允許來(lái)自任何來(lái)源的請(qǐng)求 (**)卧晓。
spring security不附帶一個(gè)具體的 UserDetailsService實(shí)現(xiàn), 我們可以使用我們的內(nèi)存中的數(shù)據(jù)庫(kù)。因此, 我們?cè)?com.auth0.samples.authapi.user包中創(chuàng)建一個(gè)名為 UserDetailsServiceImpl 的新類來(lái)提供一個(gè):
package com.auth0.samples.authapi.user;
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 static java.util.Collections.emptyList;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private ApplicationUserRepository applicationUserRepository;
public UserDetailsServiceImpl(ApplicationUserRepository applicationUserRepository) {
this.applicationUserRepository = applicationUserRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ApplicationUser applicationUser = applicationUserRepository.findByUsername(username);
if (applicationUser == null) {
throw new UsernameNotFoundException(username);
}
return new User(applicationUser.getUsername(), applicationUser.getPassword(), emptyList());
}
}
我們必須實(shí)現(xiàn)的唯一方法是 loadUserByUsername赴捞。當(dāng)用戶嘗試進(jìn)行身份驗(yàn)證時(shí), 此方法接收用戶名, 在數(shù)據(jù)庫(kù)中搜索包含該名稱的記錄, 如果找到, 則返回用戶實(shí)例。然后, 根據(jù)用戶在登錄請(qǐng)求中傳遞的憑據(jù)檢查此實(shí)例的屬性 (用戶名和密碼)郁稍。最后一個(gè)過(guò)程是通過(guò) Spring 安全框架在這個(gè)類之外執(zhí)行的赦政。
我們現(xiàn)在可以放心了, 我們的端點(diǎn)不會(huì)公開(kāi)暴露, 我們可以支持與 JWTS 在spring boot中正確的認(rèn)證和授權(quán)。要檢查所有內(nèi)容, 請(qǐng)運(yùn)行我們的應(yīng)用程序 (通過(guò) IDE 或通過(guò) gradle bootRun) 并發(fā)出以下請(qǐng)求:
# issues a GET request to retrieve tasks with no JWT
# HTTP 403 Forbidden status is expected
curl http://localhost:8080/tasks
# registers a new user
curl -H "Content-Type: application/json" -X POST -d '{
"username": "admin",
"password": "password"
}' http://localhost:8080/users/sign-up
# logs into the application (JWT is generated)
curl -i -H "Content-Type: application/json" -X POST -d '{
"username": "admin",
"password": "password"
}' http://localhost:8080/login
# issue a POST request, passing the JWT, to create a task
# remember to replace xxx.yyy.zzz with the JWT retrieved above
curl -H "Content-Type: application/json" \
-H "Authorization: Bearer xxx.yyy.zzz" \
-X POST -d '{
"description": "Buy watermelon"
}' http://localhost:8080/tasks
# issue a new GET request, passing the JWT
# remember to replace xxx.yyy.zzz with the JWT retrieved above
curl -H "Authorization: Bearer xxx.yyy.zzz" http://localhost:8080/tasks
翻譯到此結(jié)束,原文后面還有使用Auth0的一些內(nèi)容恢着,與本文無(wú)關(guān)桐愉,故不做翻譯。