通用开发技能系列:Authentication、OAuth、JWT 认证策略

云原生学习路线导航页(持续更新中)

本文是 通用开发技能系列 文章,主要对编程通用技能 Authentication、OAuth、JWT 认证策略 进行学习

1.Basic Authentication认证

  • 每个请求都需要将 用户名+密码 进行base64编码后,放在请求头的Authorization中,携带到服务端,实现HTTP基本认证
  • HTTP明码传输,不安全。一般会用HTTPS传输
  • HTTPS 在 HTTP 基础上面添加了 SSL 安全证书。但如果有人伪造CA证书,也可能造成不安全,用户名和密码直接放在请求里,太不安全了

2.OAuth认证

  • OAuth(Open Authorization)是一个用于授权的开放网络标准,允许用户授权第三方应用去访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。

  • 目前主流的 第三方应用登陆,都是使用OAuth标准做的

  • OAuth标准目前大多使用2.0版本

  • OAuth2.0授权模式(授权码模式和密码模式比较常用)

    • 授权码模式(authorization code)
    • 简化模式(implicit)
    • 密码模式(resource owner password credentials)
    • 客户端模式(client credentials)
      在这里插入图片描述
  • 更多知识学习博客

3.JWT认证

3.1.JWT是什么

  • JSON Web Token (JWT) 是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
  • 本质上来说,jwt 就是规定了一种token的格式,按照 jwt 标准生成的 token,包含3部分:header、playload、signature,每一部分都有自己的功能,因此这种token自身就携带了多种特性。

3.2.JWT的3个组成部分

  • JWT包含3部分:
    • header
      • 头部,公开的,表示这是jwt的数据。这部分会使用base64url编码,会把编码成base64后的数据中的 “+、=、/” 替换掉,这样就可以把jwt生成的token拼接在 请求的url 参数上,用于浏览器禁止cookie时使用
    • playload
      • 有效载荷,可以携带一些数据,但是不要放私密数据。这部分都是base64编码,可以解密,相当于明文
    • signature
      • 验证签名,是用来验证发送者的JWT,同时也能确保客户端传来的JWT没有被篡改
      • signature 如何鉴别客户端带过来的Token是正确的?
        • signature 是 服务端在生成token时,使用 密钥secret,对header+playload进行加密,得到的验证签名。
        • 如果 下一次请求带来的token,拿着header+playload再次进行密钥加密,得到的signature不同,说明 数据被别人篡改了。如果加密后值相等,说明没有篡改。
  • 因此,JWT标准生成的Token,实际上并不是安全的,内部都使用base64编码,也相当于明文,但是有 signature 判断数据是否被修改过

3.3.JWT的优缺点

  • JWT优点:
    • json的通用性,所以JWT是可以进行跨语言支持的。很多语言都可以使用 json 传输
    • 有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
    • 便于传输,jwt的构成非常简单,字节占用很小
    • 不需要在服务端保存会话信息, 所以它易于应用的扩展,不用考虑服务器之间的同步问题
  • JWT缺点:
    • 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
    • 一定得保护好服务端的secret私钥,该私钥非常重要。

3.4.使用 JWT + Redis 验证用户信息的过程

  • 在用户登陆成功时,服务端使用 JWT 的密钥,为该用户生成一个token。把userId存入载荷中,将token返回给前端。同时,我们会在redis中维护一个Hash表,键为token值,值为user的从数据库中查到的详细信息。
  • 下次用户再访问其他资源的时候,将token以 cookie请求头的Authorization字段url参数 的 方式一起发给服务端。
  • 服务端中,我们会在网关Gateway验证用户的登录状态。
    • 从request中取出token,使用 jwt 密钥尝试解密,如果发现 token 的 header+playload 再次加密后得到的 signature 与 token 中的 signature不再一样,说明数据被篡改了,就会拒绝服务
    • 如果验证通过,就会从载荷中取出我们事先放入的userId,将userId存到请求中,再把该请求放行。
    • 后续如果需要使用用户的详细信息,直接从请求头中获取userId,就可以到redis中查到详细信息。
  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot Security是一个非常流行的安全框架,可以帮助开发人员实现各种安全功能。其中,OAuth2和JWT是两个非常重要的安全技术,可以用于实现授权和认证。下面是一个基于Spring Boot Security的完整示例,演示如何使用OAuth2和JWT实现安全功能。 1. 创建一个Spring Boot项目 使用Spring Initializr创建一个新的Spring Boot项目,添加以下依赖: - Spring Web - Spring Security - Spring Data JPA - H2 Database - Spring Security OAuth2 - jjwt 2. 添加配置文件 在application.properties文件中添加以下配置: ``` spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=create-drop spring.h2.console.enabled=true spring.h2.console.path=/h2-console spring.security.oauth2.client.registration.myapp.client-id=myapp spring.security.oauth2.client.registration.myapp.client-secret=myappsecret spring.security.oauth2.client.registration.myapp.scope=read,write spring.security.oauth2.client.provider.myapp.authorization-uri=http://localhost:8080/oauth/authorize spring.security.oauth2.client.provider.myapp.token-uri=http://localhost:8080/oauth/token spring.security.oauth2.client.provider.myapp.user-info-uri=http://localhost:8080/userinfo spring.security.oauth2.client.provider.myapp.user-name-attribute=name ``` 这些配置将用于配置数据源、Hibernate、H2控制台和OAuth2。 3. 创建实体类和仓库 创建一个User实体类,用于表示用户信息: ```java @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column(nullable = false) private String password; @Column(nullable = false) private String email; // getters and setters } ``` 创建一个UserRepository接口,用于操作User实体类: ```java @Repository public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUsername(String username); } ``` 4. 创建用户服务 创建一个UserService接口,用于定义获取用户和创建用户的方法: ```java public interface UserService { User createUser(String username, String password, String email); Optional<User> getUserByUsername(String username); } ``` 创建一个UserServiceImpl实现UserService接口,用于实现上述方法: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserRepository userRepository; @Autowired private PasswordEncoder passwordEncoder; @Override public User createUser(String username, String password, String email) { User user = new User(); user.setUsername(username); user.setPassword(passwordEncoder.encode(password)); user.setEmail(email); return userRepository.save(user); } @Override public Optional<User> getUserByUsername(String username) { return userRepository.findByUsername(username); } } ``` 5. 配置Spring Security 创建一个WebSecurityConfig类,用于配置Spring Security: ```java @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/oauth/**").permitAll() .anyRequest().authenticated() .and() .csrf().disable() .formLogin().disable() .httpBasic().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(username -> userService.getUserByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username))) .passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ``` 这个配置类将所有请求都需要进行认证,但是允许OAuth2请求。同时,禁用了CSRF、表单登录、HTTP基本认证和会话管理。 6. 配置OAuth2 创建一个OAuth2Config类,用于配置OAuth2: ```java @Configuration @EnableAuthorizationServer public class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; @Autowired private PasswordEncoder passwordEncoder; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("myapp") .secret(passwordEncoder.encode("myappsecret")) .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("read", "write") .redirectUris("http://localhost:8081/login/oauth2/code/myapp"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(username -> userService.getUserByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username))) .accessTokenConverter(accessTokenConverter()) .pathMapping("/oauth/token", "/signin"); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("secret"); return converter; } @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } } ``` 这个配置类定义了一个OAuth2客户端,将授权码和刷新令牌作为授权类型,并允许读取和写入作用域。同时,定义了一个端点配置,将认证管理器、用户详情服务、访问令牌转换器和令牌存储配置到端点上。 7. 创建控制器 创建一个UserController类,用于处理用户相关的请求: ```java @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @PostMapping public User createUser(@RequestBody UserDto userDto) { return userService.createUser(userDto.getUsername(), userDto.getPassword(), userDto.getEmail()); } @GetMapping("/{username}") public User getUser(@PathVariable String username) { return userService.getUserByUsername(username) .orElseThrow(() -> new NotFoundException("User not found: " + username)); } } ``` 这个控制器中定义了创建用户和获取用户的方法。 8. 创建JWT过滤器 创建一个JwtFilter类,用于在请求中验证JWT令牌: ```java public class JwtFilter extends OncePerRequestFilter { private static final String AUTHORIZATION_HEADER = "Authorization"; @Autowired private JwtAccessTokenConverter accessTokenConverter; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authorizationHeader = request.getHeader(AUTHORIZATION_HEADER); if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { String token = authorizationHeader.substring(7); Authentication authentication = accessTokenConverter.extractAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); } filterChain.doFilter(request, response); } } ``` 这个过滤器会从请求头中获取JWT令牌,并使用令牌转换器验证令牌。如果验证通过,则将用户认证信息存储到Spring Security上下文中。 9. 注册JWT过滤器 在WebSecurityConfig类中,添加以下配置: ```java @Bean public JwtFilter jwtFilter() { return new JwtFilter(); } @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class); // ... } ``` 这个配置将JwtFilter注册到Spring Security过滤器链中。 10. 创建JWT工具类 创建一个JwtUtils类,用于生成和解析JWT令牌: ```java public class JwtUtils { public static final String SUBJECT = "myapp"; public static String generateToken(User user) { Map<String, Object> claims = new HashMap<>(); claims.put("sub", SUBJECT); claims.put("id", user.getId()); claims.put("username", user.getUsername()); claims.put("email", user.getEmail()); Date now = new Date(); Date expiration = new Date(now.getTime() + 60 * 60 * 1000); return Jwts.builder() .setClaims(claims) .setIssuedAt(now) .setExpiration(expiration) .signWith(SignatureAlgorithm.HS256, "secret") .compact(); } public static Long getUserIdFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey("secret") .parseClaimsJws(token) .getBody(); return claims.get("id", Long.class); } } ``` 这个工具类将用户信息存储到JWT令牌中,并使用HS256算法进行签名。 11. 创建DTO类 创建一个UserDto类,用于接收创建用户的请求: ```java public class UserDto { private String username; private String password; private String email; // getters and setters } ``` 12. 测试 启动应用程序,并使用以下命令创建一个用户: ``` curl -X POST -H 'Content-Type: application/json' -d '{"username":"test","password":"test123","email":"[email protected]"}' http://localhost:8080/users ``` 使用以下命令获取访问令牌: ``` curl -X POST -vu myapp:myappsecret http://localhost:8080/oauth/token -H 'Accept: application/json' -d 'grant_type=authorization_code&code={CODE}&redirect_uri=http://localhost:8081/login/oauth2/code/myapp' ``` 将{CODE}替换为授权码,并将返回的访问令牌用于获取用户信息: ``` curl -H 'Authorization: Bearer {ACCESS_TOKEN}' http://localhost:8080/users/test ``` 将{ACCESS_TOKEN}替换为访问令牌,在请求头中发送即可。 以上就是一个完整的Spring Boot Security OAuth2 JWT示例,演示了如何实现授权和认证

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值