前言:
如今姐妹们对“apache端口9090”大约比较着重,朋友们都需要学习一些“apache端口9090”的相关内容。那么小编也在网摘上搜集了一些有关“apache端口9090””的相关知识,希望朋友们能喜欢,看官们一起来了解一下吧!前言
通过 JWT 配合 Spring Security OAuth2 使用的方式,可以避免 每次请求 都 远程调度 认证授权服务。资源服务器 只需要从 授权服务器 验证一次,返回 JWT。返回的 JWT 包含了 用户 的所有信息,包括 权限信息。
正文
1. 什么是JWT
JSON Web Token(JWT)是一种开放的标准(RFC 7519),JWT 定义了一种 紧凑 且 自包含 的标准,旨在将各个主体的信息包装为 JSON 对象。主体信息 是通过 数字签名 进行 加密 和 验证 的。经常使用 HMAC 算法或 RSA(公钥/私钥 的 非对称性加密)算法对 JWT 进行签名,安全性很高。
紧凑型:数据体积小,可通过 POST 请求参数 或 HTTP 请求头 发送。自包含:JWT 包含了主体的所有信息,避免了 每个请求 都需要向 Uaa 服务验证身份,降低了 服务器的负载。
2. JWT的结构
JWT 的结构由三部分组成:Header(头)、Payload(有效负荷)和 Signature(签名)。因此 JWT 通常的格式是 xxxxx.yyyyy.zzzzz。
2.1. Header
Header 通常是由 两部分 组成:令牌的 类型(即 JWT)和使用的 算法类型,如 HMAC、SHA256 和 RSA。例如:
{ "typ": "JWT", "alg": "HS256"}
将 Header 用 Base64 编码作为 JWT 的 第一部分,不建议在 JWT 的 Header 中放置 敏感信息。
2.2. Payload
第二部分 Payload 是 JWT 的 主体内容部分,它包含 声明 信息。声明是关于 用户 和 其他数据 的声明。
声明有三种类型: registered、public 和 private。
Registered claimsJWT 提供了一组 预定义 的声明,它们不是 强制的,但是推荐使用。JWT 指定 七个默认 字段供选择:Public claims:可以随意定义。Private claims:用于在 同意使用 它们的各方之间 共享信息,并且不是 注册的 或 公开的 声明。
下面是 Payload 部分的一个示例:
{ "sub": "123456789", "name": "John Doe", "admin": true}
将 Payload 用 Base64 编码作为 JWT 的 第二部分,不建议在 JWT 的 Payload 中放置 敏感信息。
2.3. Signature
要创建签名部分,需要利用 秘钥 对 Base64 编码后的 Header 和 Payload 进行 加密,加密算法的公式如下:
HMACSHA256( base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret)
签名 可以用于验证 消息 在 传递过程 中有没有被更改。对于使用 私钥签名 的 token,它还可以验证 JWT 的 发送方 是否为它所称的 发送方。
3. JWT的工作方式
客户端 获取 JWT 后,对于以后的 每次请求,都不需要再通过 授权服务 来判断该请求的 用户 以及该 用户的权限。在微服务系统中,可以利用 JWT 实现 单点登录。认证流程图如下:
4. 案例工程结构
eureka-server:作为 注册服务中心,端口号为 8761。这里不再演示搭建。auth-service:作为 授权服务,授权 需要用户提供 客户端 的 client Id 和 Client Secret,以及 授权用户 的 username 和 password。这些信息 准备无误 之后,auth-service 会返回 JWT,该 JWT 包含了用户的 基本信息 和 权限点信息,并通过 RSA 私钥 进行加密。user-service:作为 资源服务,它的 资源 被保护起来,需要相应的 权限 才能访问。user-service 服务得到 用户请求 的 JWT 后,先通过 公钥 解密 JWT,得到 JWT 对应的 用户信息 和 用户权限信息,再通过 Spring Security 判断该用户是否有 权限 访问该资源。
工程原理示意图如下:
5. 构建auth-service授权服务
新建一个 auth-service 项目模块,完整的 pom.xml 文件配置如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="" xmlns:xsi="" xsi:schemaLocation=" "> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>io.github.ostenant.springcloud</groupId> <artifactId>auth-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>auth-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</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-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!--防止jks文件被mavne编译导致不可用--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <nonFilteredFileExtensions> <nonFilteredFileExtension>cert</nonFilteredFileExtension> <nonFilteredFileExtension>jks</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin> </plugins> </build></project>修改 auth-service 的配置文件 application.yml 文件如下:
spring: application: name: auth-service datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/spring-cloud-auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8 username: root password: 123456 jpa: hibernate: ddl-auto: update show-sql: trueserver: port: 9999eureka: client: serviceUrl: defaultZone:为 auth-service 配置 Spring Security 安全登录管理,用于保护 token 发放 和 验证 的资源接口。
@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserServiceDetail userServiceDetail; @Override public @Bean AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() //关闭CSRF .exceptionHandling() .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) .and() .authorizeRequests() .antMatchers("/**").authenticated() .and() .httpBasic(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceDetail).passwordEncoder(new BCryptPasswordEncoder()); }}
UserServiceDetail.java
@Servicepublic class UserServiceDetail implements UserDetailsService { @Autowired private UserDao userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username); }}
UserRepository.java
@Repositorypublic interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username);}
实体类 User 和上一篇文章的内容一样,需要实现 UserDetails 接口,实体类 Role 需要实现 GrantedAuthority 接口。
User.java
@Entitypublic class User implements UserDetails, Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column private String password; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) private List<Role> authorities; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } public void setAuthorities(List<Role> authorities) { this.authorities = authorities; } @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; }}
Role.java
@Entitypublic class Role implements GrantedAuthority { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public String getAuthority() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return name; }}新建一个配置类 OAuth2Config,为 auth-service 配置 认证服务,代码如下:
@Configuration@EnableAuthorizationServerpublic class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 将客户端的信息存储在内存中 clients.inMemory() // 配置一个客户端 .withClient("user-service") .secret("123456") // 配置客户端的域 .scopes("service") // 配置验证类型为refresh_token和password .authorizedGrantTypes("refresh_token", "password") // 配置token的过期时间为1h .accessTokenValiditySeconds(3600 * 1000); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // 配置token的存储方式为JwtTokenStore endpoints.tokenStore(tokenStore()) // 配置用于JWT私钥加密的增强器 .tokenEnhancer(jwtTokenEnhancer()) // 配置安全认证管理 .authenticationManager(authenticationManager); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtTokenEnhancer()); } @Bean protected JwtAccessTokenConverter jwtTokenEnhancer() { // 配置jks文件 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("fzp-jwt.jks"), "fzp123".toCharArray()); JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setKeyPair(keyStoreKeyFactory.getKeyPair("fzp-jwt")); return converter; }}生成用于 Token 加密的 私钥文件 fzp-jwt.jks
jks 文件的生成需要使用 Java keytool 工具,保证 Java 环境变量没问题,输入命令如下:
$ keytool -genkeypair -alias fzp-jwt -validity 3650 -keyalg RSA -dname "CN=jwt,OU=jtw,O=jwt,L=zurich,S=zurich, C=CH" -keypass fzp123 -keystore fzp-jwt.jks -storepass fzp123
其中,-alias 选项为 别名,-keyalg 为 加密算法,-keypass 和 -storepass 为 密码选项,-keystore 为 jks 的 文件名称,-validity 为配置 jks 文件 过期时间(单位:天)。
生成的 jks 文件作为 私钥,只允许 授权服务 所持有,用作 加密生成 JWT。把生成的 jks 文件放到 auth-service 模块的 src/main/resource 目录下即可。
生成用于 JWT 解密的 公钥
对于 user-service 这样的 资源服务,需要使用 jks 的 公钥 对 JWT 进行 解密。获取 jks 文件的 公钥 的命令如下:
$ keytool -list -rfc --keystore fzp-jwt.jks | openssl x509 -inform pem -pubkey
这个命令要求安装 openSSL 下载地址,然后手动把安装的 openssl.exe 所在目录配置到 环境变量。
输入密码 fzp123 后,显示的信息很多,只需要提取 PUBLIC KEY,即如下所示:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlCFiWbZXIb5kwEaHjW+/
7J4b+KzXZffRl5RJ9rAMgfRXHqGG8RM2Dlf95JwTXzerY6igUq7FVgFjnPbexVt3
vKKyjdy2gBuOaXqaYJEZSfuKCNN/WbOF8e7ny4fLMFilbhpzoqkSHiR+nAHLkYct
OnOKMPK1SwmvkNMn3aTEJHhxGh1RlWbMAAQ+QLI2D7zCzQ7Uh3F+Kw0pd2gBYd8W
+DKTn1Tprugdykirr6u0p66yK5f1T9O+LEaJa8FjtLF66siBdGRaNYMExNi21lJk
i5dD3ViVBIVKi9ZaTsK9Sxa3dOX1aE5Zd5A9cPsBIZ12spYgemfj6DjOw6lk7jkG
9QIDAQAB
-----END PUBLIC KEY-----
新建一个 public.cert 文件,将上面的 公钥信息 复制到 public.cert 文件中并保存。并将文件放到 user-service 等 资源服务 的 src/main/resources 目录下。至此 auth-service 搭建完毕。
在 pom.xml 中配置 jks 文件后缀过滤器
maven 在项目编译时,可能会将 jks 文件 编译,导致 jks 文件 乱码,最后不可用。需要在 pom.xml 文件中添加以下内容:
<!-- 防止jks文件被maven编译导致不可用 --><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <nonFilteredFileExtensions> <nonFilteredFileExtension>cert</nonFilteredFileExtension> <nonFilteredFileExtension>jks</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration></plugin>最后在启动类上配置 @EnableEurekaClient 注解开启服务注册功能。
@EnableEurekaClient@SpringBootApplicationpublic class AuthServiceApplication { public static void main(String[] args) { SpringApplication.run(AuthServiceApplication.class, args); }}
6. 构建user-service资源服务
新建一个 user-service 项目模块,完整的 pom.xml 文件配置如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="" xmlns:xsi="" xsi:schemaLocation=" "> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>io.github.ostenant.springcloud</groupId> <artifactId>user-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>user-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</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.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>修改 user-service 的配置文件 application.yml,配置 应用名称 为 user-service,端口号 为 9090。另外,需要配置 feign.hystrix.enable 为 true,即开启 Feign 的 Hystrix 功能。完整的配置代码如下:
server: port: 9090eureka: client: service-url: defaultZone: : application: name: user-service datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/spring-cloud-auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8 username: root password: 123456 jpa: hibernate: ddl-auto: update show-sql: truefeign: hystrix: enabled: true配置 资源服务
注入 JwtTokenStore 类型的 Bean,同时初始化 JWT 转换器 JwtAccessTokenConverter,设置用于解密 JWT 的 公钥。
@Configurationpublic class JwtConfig { @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Bean @Qualifier("tokenStore") public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter); } @Bean public JwtAccessTokenConverter jwtTokenEnhancer() { // 用作JWT转换器 JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); Resource resource = new ClassPathResource("public.cert"); String publicKey; try { publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); } catch (IOException e) { throw new RuntimeException(e); } //设置公钥 converter.setVerifierKey(publicKey); return converter; }}
配置 资源服务 的认证管理,除了 注册 和 登录 的接口之外,其他的接口都需要 认证。
@Configuration@EnableResourceServerpublic class ResourceServerConfig extends ResourceServerConfigurerAdapter{ @Autowired private TokenStore tokenStore; @Override public void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/user/login","/user/register").permitAll() .antMatchers("/**").authenticated(); } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.tokenStore(tokenStore); }}
新建一个配置类 GlobalMethodSecurityConfig,通过 @EnableGlobalMethodSecurity 注解开启 方法级别 的 安全验证。
@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true)public class GlobalMethodSecurityConfig {}实现用户注册接口
拷贝 auth-service 模块的 User、Role 和 UserRepository 三个类到本模块。在 Service 层的 UserService 编写一个 插入用户 的方法,代码如下:
@Servicepublic class UserServiceDetail { @Autowired private UserRepository userRepository; public User insertUser(String username,String password){ User user=new User(); user.setUsername(username); user.setPassword(BPwdEncoderUtil.BCryptPassword(password)); return userRepository.save(user); }}
配置用于用户密码 加密 的工具类 BPwdEncoderUtil:
public class BPwdEncoderUtil { private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); public static String BCryptPassword(String password){ return encoder.encode(password); } public static boolean matches(CharSequence rawPassword, String encodedPassword){ return encoder.matches(rawPassword,encodedPassword); }}
实现一个 用户注册 的 API 接口 /user/register,代码如下:
@RestController@RequestMapping("/user")public class UserController { @Autowired UserServiceDetail userServiceDetail; @PostMapping("/register") public User postUser(@RequestParam("username") String username, @RequestParam("password") String password){ return userServiceDetail.insertUser(username, password); }}实现用户登录接口
在 Service 层的 UserServiceDetail 中添加一个 login() 方法,代码如下:
@Servicepublic class UserServiceDetail { @Autowired private AuthServiceClient client; public UserLoginDTO login(String username, String password) { // 查询数据库 User user = userRepository.findByUsername(username); if (user == null) { throw new UserLoginException("error username"); } if(!BPwdEncoderUtil.matches(password,user.getPassword())){ throw new UserLoginException("error password"); } // 从auth-service获取JWT JWT jwt = client.getToken("Basic dXNlci1zZXJ2aWNlOjEyMzQ1Ng==", "password", username, password); if(jwt == null){ throw new UserLoginException("error internal"); } UserLoginDTO userLoginDTO=new UserLoginDTO(); userLoginDTO.setJwt(jwt); userLoginDTO.setUser(user); return userLoginDTO; }}
AuthServiceClient 作为 Feign Client,通过向 auth-service 服务接口 /oauth/token 远程调用获取 JWT。在请求 /oauth/token 的 API 接口中,需要在 请求头 传入 Authorization 信息,认证类型 ( grant_type )、用户名 ( username ) 和 密码 ( password ),代码如下:
@FeignClient(value = "auth-service", fallback = AuthServiceHystrix.class)public interface AuthServiceClient { @PostMapping("/oauth/token") JWT getToken(@RequestHeader("Authorization") String authorization, @RequestParam("grant_type") String type, @RequestParam("username") String username, @RequestParam("password") String password);}
其中,AuthServiceHystrix 为 AuthServiceClient 的 熔断器,代码如下:
@Componentpublic class AuthServiceHystrix implements AuthServiceClient { private static final Logger LOGGER = LoggerFactory.getLogger(AuthServiceHystrix.class); @Override public JWT getToken(String authorization, String type, String username, String password) { LOGGER.warn("Fallback of getToken is executed") return null; }}
JWT 包含了 access_token、token_type 和 refresh_token 等信息,代码如下:
public class JWT { private String access_token; private String token_type; private String refresh_token; private int expires_in; private String scope; private String jti; public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this.access_token = access_token; } public String getToken_type() { return token_type; } public void setToken_type(String token_type) { this.token_type = token_type; } public String getRefresh_token() { return refresh_token; } public void setRefresh_token(String refresh_token) { this.refresh_token = refresh_token; } public int getExpires_in() { return expires_in; } public void setExpires_in(int expires_in) { this.expires_in = expires_in; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public String getJti() { return jti; } public void setJti(String jti) { this.jti = jti; }}
UserLoginDTO 包含了一个 User 和一个 JWT 成员属性,用于返回数据的实体:
public class UserLoginDTO { private JWT jwt; private User user; public JWT getJwt() { return jwt; } public void setJwt(JWT jwt) { this.jwt = jwt; } public User getUser() { return user; } public void setUser(User user) { this.user = user; }}
登录异常类 UserLoginException
public class UserLoginException extends RuntimeException { public UserLoginException(String message) { super(message); }}
全局异常处理 切面类 ExceptionHandle
@ControllerAdvice@ResponseBodypublic class ExceptionHandler { @ExceptionHandler(UserLoginException.class) public ResponseEntity<String> handleException(Exception e) { return new ResponseEntity(e.getMessage(), HttpStatus.OK); }}
在 Web 层的 UserController 类中新增一个登录的 API 接口 /user/login 如下:
@PostMapping("/login")public UserLoginDTO login(@RequestParam("username") String username, @RequestParam("password") String password) { return userServiceDetail.login(username,password);}为了测试 用户权限,再新增一个 /foo 接口,该接口需要 ROLE_ADMIN 权限才能正常访问。
@RequestMapping(value = "/foo", method = RequestMethod.GET)@PreAuthorize("hasAuthority('ROLE_ADMIN')")public String getFoo() { return "i'm foo, " + UUID.randomUUID().toString();}最后在应用的启动类上使用注解 @EnableFeignClients 开启 Feign 的功能即可。
@SpringBootApplication@EnableFeignClients@EnableEurekaClientpublic class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); }}
依次启动 eureka-service,auth-service 和 user-service 三个服务。
7. 使用Postman测试
注册一个用户,返回注册成功信息使用用户名密码登录获取 JWT复制上面的 access_token 到 header 头部,请求需要 用户权限 的 /user/foo 接口
"Authorization": "Bearer {access_token}"
因为没有权限,访问被拒绝。在数据库手动添加 ROLE_ADMIN 权限,并与该用户关联。重新登录并获取 JWT,再次请求 /user/foo 接口。
总结
在本案例中,用户通过 登录接口 来获取 授权服务 加密后的 JWT。用户成功获取 JWT 后,在以后每次访问 资源服务 的请求中,都需要携带上 JWT。资源服务 通过 公钥解密 JWT,解密成功 后可以获取 用户信息 和 权限信息,从而判断该 JWT 所对应的 用户 是谁,具有什么 权限。
优点:
获取一次 Token,多次使用,资源服务 不再每次访问 授权服务 该 Token 所对应的 用户信息 和用户的 权限信息。
缺点:
一旦 用户信息 或者 权限信息 发生了改变,Token 中存储的相关信息并 没有改变,需要 重新登录 获取新的 Token。就算重新获取了 Token,如果原来的 Token 没有过期,仍然是可以使用的。一种改进方式是在登录成功后,将获取的 Token 缓存 在 网关上。如果用户的 权限更改,将 网关 上缓存的 Token 删除。当请求经过 网关,判断请求的 Token 在 缓存 中是否存在,如果缓存中不存在该 Token,则提示用户 重新登录。
标签: #apache端口9090