SpringBoot+Spring Security+JWT实现RESTful Api权限控制的方法

下面是“SpringBoot+Spring Security+JWT实现RESTful Api权限控制的方法”的完整攻略:

简介

本篇攻略介绍如何使用SpringBoot、Spring Security、JWT实现RESTful Api权限控制。Spring Security可以提供强大的身份验证和授权功能,而JWT可以用于生成安全的令牌。本攻略将介绍如何将这两个框架结合使用,以便在RESTful Api应用程序中实现身份验证和访问控制功能。

环境和依赖

本文使用的环境和依赖如下:

环境:

  • JDK 1.8 或以上
  • SpringBoot 2.5.x
  • Spring Security 5.5.x

依赖:

<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>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>7.0.1.Final</version>
    </dependency>
</dependencies>

代码实现

配置Spring Security

首先,我们需要配置Spring Security,以便在应用程序中启用安全性。我们需要创建一个SecurityConfig类,该类将扩展WebSecurityConfigurerAdapter类,并覆盖configure方法,以配置Spring Security。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated()
            .and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

在该类中,我们定义了PasswordEncoder,该类用于对密码进行加密。我们还覆盖了configure方法,并使用UserService和PasswordEncoder配置了身份验证。最后,我们定义了HttpSecurity对象,以便在应用程序中启用授权。我们定义了url映射,任何未经授权的请求都将被阻挡,并向用户返回403错误。我们为所有的请求配置了JwtAuthenticationFilter,以确保它们都被验证。

创建JwtToken工具

接下来,我们需要创建JwtToken工具类,该类将被用于生成和解析jwt token。这里我们使用jjwt库实现JwtToken。

@Component
public class JwtTokenUtil {

    private static final String SECRET_KEY = "my-secret-key";

    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;

    @Value("${jwt.tokenExpirationTime}")
    private Long tokenExpirationTime;

    public String generateToken(UserDetails userDetails) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + tokenExpirationTime);

        return Jwts.builder().setSubject(userDetails.getUsername())
            .setIssuedAt(now)
            .setExpiration(expiryDate)
            .signWith(SIGNATURE_ALGORITHM, SECRET_KEY)
            .compact();
    }

    public String getUsernameFromToken(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();
    }

    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    private boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Date getExpirationDateFromToken(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration();
    }
}

在该类中,我们使用了@Value注解来获取配置文件中的tokenExpirationTime属性。然后,我们定义了generateToken方法,该方法根据传入的用户细节生成jwt token。我们还定义了getUsernameFromToken方法,该方法从jwt token中获取用户名。最后,我们定义了validateToken方法,该方法用于验证jwt token是否有效。

创建JwtAuthenticationFilter类

接下来,我们需要创建一个JwtAuthenticationFilter类,该类将被用于解析jwt token,并使用Spring Security进行身份验证。该类将继承OncePerRequestFilter类,并覆盖doFilterInternal方法。

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        final String requestHeader = request.getHeader("Authorization");
        String username = null;
        String jwtToken = null;

        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            jwtToken = requestHeader.substring(7);
            username = jwtTokenUtil.getUsernameFromToken(jwtToken);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }

        filterChain.doFilter(request, response);
    }
}

在该类中,我们首先检查HTTP请求头中是否存在Authorization头,并分离出Bearer token。然后,我们使用JwtTokenUtil获取用户的用户名,并尝试使用它来找到用户的详细信息。最后,我们使用JwtTokenUtil验证jwt token是否有效,并使用UsernamePasswordAuthenticationToken设置用户的详细信息。

创建JwtAuthenticationEntryPoint类

我们还需要创建一个JwtAuthenticationEntryPoint类,该类将被用于处理身份验证错误,以确保所有未经验证的用户都被拦截。

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}

在该类中,我们覆盖了commence方法,该方法用于处理身份验证错误。我们向用户返回HTTP 401错误。

创建UserService类

我们还需要创建一个UserService类,该类将为Spring Security获取用户详细信息。

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);

        if (user == null) {
            throw new UsernameNotFoundException(username);
        }

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                new ArrayList<>());
    }
}

在该类中,我们实现了UserDetailsService,并覆盖了loadUserByUsername方法,该方法用于从数据库中获取用户详细信息。

创建RestController类

最后,我们还需要创建一个RestController类,该类包含需要进行身份验证的RESTful API。

@RestController
@RequestMapping("/api")
public class MainController {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserService userService;

    @PostMapping("/auth/login")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {

        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwtToken = jwtTokenUtil.generateToken(userService.loadUserByUsername(loginRequest.getUsername()));

        return ResponseEntity.ok(new JwtAuthenticationResponse(jwtToken));
    }

    @GetMapping("/user")
    public ResponseEntity<?> getUser(HttpServletRequest request) {
        String jwtToken = request.getHeader("Authorization").substring(7);

        return ResponseEntity.ok(jwtTokenUtil.getUsernameFromToken(jwtToken));
    }
}

在该类中,我们为登录API定义了一个POST 映射(/api/auth/login),该映射将处理登录请求。我们还为获取用户API定义了一个GET映射(/api/user),该映射将返回当前已登录用户的用户名。

示例

登录API

我们可以使用curl命令或Postman软件来测试登录API,请求的body数据为:

{
    "username": "user",
    "password": "password"
}
$ curl -X POST -H "Content-Type: application/json" \
    -d '{"username": "user", "password": "password"}' \
    http://localhost:8080/api/auth/login

请求成功后,响应内容如下:

{
    "accessToken": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiZXhwIjoxNjIzNDk5NjM0LCJpYXQiOjE2MjM0NzM2MzR9.u6FGd1fxn6e9zGNNSmNirLfqD9r8kHLB_crziwZO6Zj0WfOsA5SHytUdGU-FL7E7dqqGbDQgK5iK0suEZcVUwA"
}

获取用户API

我们可以使用curl命令或Postman软件来测试获取用户API,请求的Header中需要包含刚刚获取到的accessToken,示例如下:

$ curl -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiZXhwIjoxNjIzNDk5NjM0LCJpYXQiOjE2MjM0NzM2MzR9.u6FGd1fxn6e9zGNNSmNirLfqD9r8kHLB_crziwZO6Zj0WfOsA5SHytUdGU-FL7E7dqqGbDQgK5iK0suEZcVUwA" \
    http://localhost:8080/api/user

请求成功后,响应内容如下:

"user"

结束语

本篇攻略中介绍了如何使用SpringBoot、Spring Security、JWT实现RESTful Api权限控制,包括配置Spring Security,创建JwtToken工具类,创建JwtAuthenticationFilter类,创建JwtAuthenticationEntryPoint类,创建UserService类,创建RestController类,并提供了两个示例。希望这篇攻略能够帮助你实现RESTful API的权限控制。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot+Spring Security+JWT实现RESTful Api权限控制的方法 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • SpringBoot整合Mybatis-plus案例及用法实例

    SpringBoot是一个非常流行的Java开发框架,而Mybatis-Plus则是Mybatis框架的一个插件,提供了更加便捷的CRUD操作以及更优雅的SQL语句写法。下面就为大家详细讲解如何整合SpringBoot和Mybatis-Plus,并提供两个简单的用法示例。 1. 初始化SpringBoot项目 首先我们需要初始化一个SpringBoot项目,…

    Java 2023年5月20日
    00
  • Android UI设计与开发之ViewPager仿微信引导界面以及动画效果

    Android UI设计与开发之ViewPager仿微信引导界面以及动画效果 一. 引言 Android应用程序作为目前最主流的平台之一,UI设计的重要性越来越突出。ViewPager是Android UI界面设计中重要的一个组件,可以轻松实现左右滑动来切换不同View的效果,因此被广泛应用于app引导界面的设计中。 本文将详细讲解如何使用ViewPager…

    Java 2023年6月1日
    00
  • Java实现广度优先遍历的示例详解

    Java实现广度优先遍历的示例详解 什么是广度优先遍历 广度优先遍历(Breadth First Search, BFS)是一种图形的遍历算法,其遍历能力基于层次高效地访问相邻节点,并按顺序访问节点。这种方式即宽度优先,图形遍历的起点为根节点,相关的数据结构是队列。 广度优先遍历的应用 广度优先遍历算法在许多领域都有应用,比如: 寻找最短路径 二叉树搜索 网…

    Java 2023年5月19日
    00
  • JS+DIV+CSS实现仿表单下拉列表效果

    下面是JS+DIV+CSS实现仿表单下拉列表效果的完整攻略: 一、实现思路 在HTML中定义一个表单元素 使用CSS美化表单元素的样式 使用JavaScript控制下拉列表的显示与隐藏,并将选中的值显示到表单元素中 二、具体实现步骤 1. HTML结构 在HTML中定义一个表单元素,并使用div元素模拟下拉列表的选项: <div class=&quot…

    Java 2023年6月15日
    00
  • 基于springMvc+hibernate的web application的构建

    下面是关于基于Spring MVC和Hibernate的Web应用程序构建的完整攻略,包含两个示例说明。 基于Spring MVC和Hibernate的Web应用程序构建 Spring MVC和Hibernate是Java Web应用程序开发中常用的框架。在本文中,我们将介绍如何使用这两个框架来构建一个Web应用程序。 步骤1:添加依赖 首先,我们需要在po…

    Java 2023年5月17日
    00
  • 浅谈SpringBoot优化技巧

    SpringBoot优化技巧 SpringBoot是目前广泛应用于Java web开发中的一款优秀框架,其简化了开发流程、提高了开发效率、提升了代码的可维护性,在实际开发中应用广泛。但是,一些不良操作或者技术栈的选择不当,会导致性能问题出现。 为了解决这些问题,我们需要对SpringBoot进行优化。在本文中,我将详细介绍一些SpringBoot的优化技巧,…

    Java 2023年5月15日
    00
  • SpringMVC参数传递之基本数据类型和复杂对象说明

    SpringMVC参数传递之基本数据类型和复杂对象说明 在SpringMVC中,参数传递是非常重要的,它可以帮助我们将数据从页面传递到控制器中进行处理。本文将详细介绍SpringMVC中参数传递的两种方式:基本数据类型和复杂对象,并提供两个示例说明。 基本数据类型参数传递 在SpringMVC中,我们可以使用基本数据类型来传递参数。以下是一个简单的示例,它使…

    Java 2023年5月17日
    00
  • java springmvc乱码解决归纳整理详解

    Java Spring MVC 是一种非常流行的 Java Web 开发框架。它提供了许多特性和强大的功能,但是在处理中文文本等需要编码转换的场景中,往往会遇到乱码问题。本篇攻略将详细讲解如何解决 Java Spring MVC 中的乱码问题。 1. 请求编码解决 一般情况下,在处理 HTTP 请求时,浏览器会设置请求的编码格式。但是如果请求头中没有指定编码…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部