Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码

下面是详细的讲解“Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码”的攻略。

1. Spring Security简介

Spring Security 是一个强大且高度可定制的身份验证和访问控制框架,与 Spring 应用程序无缝集成,具有广泛的可用插件和扩展点以满足几乎任何身份验证和授权要求。Spring Security 提供的安全功能包括认证、授权、防止 CSRF、会话管理等。

2. Springboot+Spring Security实现前后端分离登录认证及权限控制

下面是Springboot+Spring Security实现前后端分离登录认证及权限控制的步骤。

2.1 修改pom.xml文件

添加以下依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>

2.2 编写Spring Security配置类

新建一个类SecurityConfig,使用@EnableWebSecurity注解开启Spring Security支持,并继承WebSecurityConfigurerAdapter类。重写configure方法,设置HttpSecurity配置。示例代码如下:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthenticationEntryPoint unauthorizedHandler;

    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(this.userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

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

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // 禁用 CSRF
                .csrf().disable()

                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()

                // 不创建会话
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

                .authorizeRequests()
                // 允许匿名访问的URL
                .antMatchers(
                        HttpMethod.GET,
                        "/",
                        "/v2/api-docs",           // swagger api json
                        "/swagger-resources/**",  // swagger 资源
                        "/swagger-ui.html",
                        "/webjars/**"
                ).permitAll()
                // 所有请求需要身份认证
                .anyRequest().authenticated();

        // 添加JWT过滤器
        JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter();
        jwtAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
        httpSecurity.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        //添加自定义的AccessDecisionManager
        httpSecurity.authorizeRequests().accessDecisionManager(updateAccessDecisionManager());
    }

    private AccessDecisionManager updateAccessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters
                = Arrays.asList(new RoleVoter(), new AuthenticatedVoter());

        return new AffirmativeBased(decisionVoters);
    }
}

2.3 编写Spring Security过滤器

新建一个类JwtAuthenticationFilter,继承OncePerRequestFilter类,实现JSON Web Token(JWT)的认证。具体实现步骤如下:

  1. 从请求头获取JWT,判断是否存在。
  2. 如果JWT存在,使用JWT解码出载荷中的用户名。
  3. 从数据库查询该用户名对应的用户对象。
  4. 构造一个Token对象(UsernamePasswordAuthenticationToken)。
  5. 将Token传入SecurityContextHolder中。

示例代码如下:

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = getJwtFromRequest(httpServletRequest);

            if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
                String username = jwtTokenProvider.getUsernameFromJWT(jwt);

                UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication in security context", ex);
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader(JwtTokenProvider.AUTHORIZATION_HEADER);

        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(JwtTokenProvider.TOKEN_PREFIX)) {
            return bearerToken.substring(JwtTokenProvider.TOKEN_PREFIX.length());
        }
        return null;
    }
}

2.4 编写JWT认证的Provider

新建一个类JwtTokenProvider,实现JWT的生成和解码。具体实现步骤如下:

  1. 使用JWT的默认算法HMAC256,使用自定义加密的KEY。
  2. 生成JWT时,使用当前的时间和有效期作为payload。
  3. 解码JWT时,使用JWT的包含的信息对JWT进行解码。

示例代码如下:

@Component
public class JwtTokenProvider {

    public static final String AUTHORIZATION_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Bearer ";
    private final String secretKey = "mySecretKey";

    public String generateToken(Authentication authentication) {
        CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();

        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + 3600 * 1000);

        return Jwts.builder()
                .setSubject(customUserDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, secretKey)
                .compact();
    }

    public String getUsernameFromJWT(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody();

        return claims.getSubject();
    }

    public boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException ex) {
            logger.error("Invalid JWT signature");
        } catch (MalformedJwtException ex) {
            logger.error("Invalid JWT token");
        } catch (ExpiredJwtException ex) {
            logger.error("Expired JWT token");
        } catch (UnsupportedJwtException ex) {
            logger.error("Unsupported JWT token");
        } catch (IllegalArgumentException ex) {
            logger.error("JWT claims string is empty.");
        }
        return false;
    }
}

2.5 编写自定义UserDetailsService类

新建一个类CustomUserDetailsService,实现Spring Security的UserDetailsService接口。具体实现步骤如下:

  1. 从数据库获取用户信息(适配自己的数据库)。
  2. 如果查询到了用户信息,则构造一个Spring Security的UserDetails对象。

示例代码如下:

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional
    public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException {
        User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail)
                .orElseThrow(() ->
                        new UsernameNotFoundException("User not found with username or email : " + usernameOrEmail)
                );

        return CustomUserDetails.create(user);
    }
}

2.6 编写Controller

新建一个类UserController,实现Spring MVC的Controller。示例代码如下:

@RestController
@RequestMapping("/api/v1/user")
public class UserController {

    @PreAuthorize("hasAnyRole('ADMIN', 'USER')")
    @GetMapping("/info")
    public String getUserInfo(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
        return "Hello, " + customUserDetails.getUsername();
    }
}

3. 示例

3.1 登录认证

前端使用POST请求 /login 接口,将用户名和密码发送到后端。后端使用Spring Security的实现类进行认证,认证成功后生成JWT,并将JWT发送到前端。

@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private AuthenticationManager authenticationManager;

    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsernameOrEmail(),
                        loginRequest.getPassword()
                )
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwt = jwtTokenProvider.generateToken(authentication);
        return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
    }
}

3.2 访问控制

在Controller中使用@PreAuthorize注解控制方法的访问权限。示例代码如下:

@RestController
@RequestMapping("/api/v1/user")
public class UserController {

    @PreAuthorize("hasAnyRole('ADMIN', 'USER')")
    @GetMapping("/info")
    public String getUserInfo(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
        return "Hello, " + customUserDetails.getUsername();
    }
}

在loadUserByUsername方法中将查询到的用户信息封装成Spring Security的UserDetails对象,并构造了一个CustomUserDetails对象。示例代码如下:

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional
    public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException {
        User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail)
                .orElseThrow(() ->
                        new UsernameNotFoundException("User not found with username or email : " + usernameOrEmail)
                );

        return CustomUserDetails.create(user);
    }
}

4. 总结

本文详细讲解了如何使用Springboot+Spring Security实现前后端分离登录认证及权限控制,并提供了示例代码。在实践中,需要根据自己的具体需求和业务逻辑进行调整和实现。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码 - Python技术站

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

相关文章

  • JavaSpringBoot报错“MissingServletRequestParameterException”的原因和处理方法

    原因 “MissingServletRequestParameterException” 错误通常是以下原因引起的: 缺少请求参数:如果您的请求参数缺失,则可能会出现此错误。在这种情况下,您需要检查您的请求参数并确保它们存在。 请求参数类型不正确:如果您的请求参数类型不正确,则可能会出现此错误。在这种情况下,您需要检查您的请求参数类型并确保它们正确。 解决办…

    Java 2023年5月4日
    00
  • 一文带你学会Java网络编程

    一文带你学会Java网络编程攻略 什么是网络编程 网络编程指的是利用计算机网络实现不同计算机间的数据通信。网络编程需要使用网络协议和Socket套接字等技术。Java语言提供了丰富的网络编程API,开发者们可以利用Java语言实现各种网络通信。 Java网络编程的核心技术 协议 网络编程中最关键的技术就是各种网络协议:TCP、UDP、HTTP、SMTP、FT…

    Java 2023年5月19日
    00
  • SpringBoot集成阿里巴巴Druid监控的示例代码

    下面是关于SpringBoot集成阿里巴巴Druid监控的示例代码的完整攻略。本文中包含以下内容: 什么是阿里巴巴Druid监控。 阿里巴巴Druid监控的优势与特点。 SpringBoot集成阿里巴巴Druid监控的步骤。 两个示例代码。 什么是阿里巴巴Druid监控 阿里巴巴Druid监控是一款对数据库进行监控的工具。它提供了丰富的监控数据和可视化界面,…

    Java 2023年5月20日
    00
  • SpringBoot2.x 整合Spring-Session实现Session共享功能

    下面我将详细讲解“SpringBoot2.x 整合Spring-Session实现Session共享功能”的完整攻略。 1. 什么是Spring Session Spring Session是Spring框架提供的一个解决方案,用于替换Java Web中使用的HttpSession。 Spring Session将HttpSession存储在集中式存储中,如…

    Java 2023年5月19日
    00
  • SpringSecurity oAuth2.0的四种模式(小结)

    SpringSecurity OAuth2.0的四种模式 SpringSecurity OAuth2.0提供了四种授权模式,分别是Authorization Code、Implicit、Resource Owner Password Credentials和Client Credentials。下面将分别对这四种授权模式进行详细讲解。 Authorizati…

    Java 2023年5月20日
    00
  • mybatis中批量插入的两种方式(高效插入)

    在MyBatis中,批量插入是一种常见的高效插入方式,可以大大减少操作数据库的次数,提高插入效率。本文将详细讲解MyBatis中批量插入的两种方式及使用方法。 使用JDBC批量插入 MyBatis底层封装了JDBC,所以可以使用JDBC的批量操作功能进行批量插入。具体实现步骤如下: 创建数据库表 假设我们要插入的表是user,可以通过以下语句创建表: CRE…

    Java 2023年5月20日
    00
  • 常见的 JVM 性能分析工具有哪些?

    以下是关于常见的 JVM 性能分析工具的完整使用攻略: 常见的 JVM 性能分析工具 JVM 性能分析工具是用来分析 Java 程序在 JVM 上的性能表现的工具。常见的 JVM 性能分析工具包括以下几种: 1. JConsole JConsole 是 JDK 自带的一款监控工具,可以监控 JVM 的内存、线程、类、CPU 等信息。通过 JConsole,可…

    Java 2023年5月12日
    00
  • Java实现二叉树的深度优先遍历和广度优先遍历算法示例

    针对“Java实现二叉树的深度优先遍历和广度优先遍历算法示例”的题目,下面给出完整的攻略。 什么是二叉树深度优先遍历和广度优先遍历 在学习Java实现二叉树深度优先遍历和广度优先遍历之前,我们需要先了解什么是二叉树深度优先遍历和广度优先遍历。 二叉树深度优先遍历是先访问根节点,再遍历左子树,最后再遍历右子树。而广度优先遍历则是一层一层地访问树节点,即先访问第…

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