详解Ruoyi-Vue的登录Springsecurity+JWT

Ruoyi-Vue使用Spring Security和JWT(JSON Web Token)实现了一套基于角色的权限管理和认证授权系统。下面将详细讲解这一实现过程。

  1. 依赖引入:

在后端的pom.xml文件中,引入spring-securityjjwt相关的依赖。

<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>

 2.配置Spring Security:

在后端项目中创建一个名为WebSecurityConfig的配置类,继承WebSecurityConfigurerAdapter,并添加以下配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // 省略其他配置

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/login", "/register", "/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
        http.headers().cacheControl();
    }
}

这段代码配置了允许无需登录即可访问的URL(如:登录、注册等),以及需要登录才能访问的其他URL。它还禁用了CSRF(跨站请求伪造)保护,添加了自定义的JWT认证过滤器,并设置了无状态的会话策略。

JWT工具类:

创建一个名为JwtTokenUtil的工具类,用于生成和解析JWT。这个类包含了如下方法:

  • generateToken(UserDetails userDetails):根据用户信息生成JWT
  • getUsernameFromToken(String token):从JWT中获取用户名
  • validateToken(String token, UserDetails userDetails)
  • 验证JWT是否有效用户认证和授权:

创建一个名为JwtUserDetailsService的服务类,实现UserDetailsService接口,实现loadUserByUsername方法。这个方法通过用户名查询数据库获取用户信息,并将用户信息封装成JwtUser对象。

@Service
public class JwtUserDetailsService implements UserDetailsService {
    @Autowired
    private SysUserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser user = userService.selectUserByLoginName(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found with username: " + username);
        }
        return createJwtUser(user);
    }
}

JWT过滤器:

创建一个名为JwtAuthenticationTokenFilter的过滤器,继承OncePerRequestFilter。在doFilterInternal方法中,从请求头中获取JWT,然后使用JwtTokenUtil工具类解析JWT,获取用户名。接着,调用`JwtUserDetailsService加载用户信息,并将其设置到SecurityContextHolder中,以便后续的认证授权操作。请注意,仅当请求头中包含有效的JWT时,此过滤器才会执行这些操作。

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String authToken = request.getHeader("Authorization");
        if (authToken != null && authToken.startsWith("Bearer ")) {
            authToken = authToken.substring(7);
        }

        String username = jwtTokenUtil.getUsernameFromToken(authToken);

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

            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }

        chain.doFilter(request, response);
    }
}

 

登录控制器:

创建一个名为AuthController的控制器,实现登录功能。在登录方法中,首先调用AuthenticationManager进行身份认证,然后使用JwtTokenUtil生成JWT,并将其作为响应数据返回给前端。

@RestController
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private JwtUserDetailsService userDetailsService;

    @PostMapping("/login")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest) {
        authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());

        UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
        String token = jwtTokenUtil.generateToken(userDetails);

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

    private void authenticate(String username, String password) {
        try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (Exception e) {
            throw new AuthenticationServiceException("Invalid username or password.", e);
        }
    }
}
  1. 前端实现

在前端项目中,使用axios库实现与后端的通信。首先,在登录页面上收集用户名和密码,然后通过axios向后端发送POST请求,将用户名和密码作为请求数据。如果登录成功,将返回的JWT存储在浏览器的localStorage中,并将其添加到后续请求的Authorization头中。

// 发送登录请求
axios.post('/login', {
    username: this.username,
    password: this.password
}).then(response => {
    localStorage.setItem('token', response.data.token);
    // 添加到请求头
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + response.data.token;
    // 跳转到其他页面
});
  1. 基于角色的权限管理:在JwtUserDetailsService中,从数据库中获取用户的角色信息,并将其封装到JwtUser对象中。这些角色信息会存储在生成的JWT中,用于后续的权限检查。在配置类WebSecurityConfig中,通过添加@EnableGlobalMethodSecurity(prePostEnabled = true)注解来启用基于角色的权限管理。这样,在控制器类中,你可以使用@PreAuthorize注解来限制某个方法只能被具有特定角色的用户访问。例如:
@RestController
@RequestMapping("/api/users")
public class UserController {
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @GetMapping("/all")
    public ResponseEntity<List<User>> getAllUsers() {
        // ...
    }
}
  1. JWT刷新:

JWT的有效期是有限的,为了避免用户在登录后不久就需要重新登录,你可以实现JWT的刷新机制。在AuthController中,添加一个用于刷新JWT的接口:

@GetMapping("/refresh")
public ResponseEntity<?> refreshAndGetAuthenticationToken(HttpServletRequest request) {
    String authToken = request.getHeader("Authorization");
    if (authToken != null && authToken.startsWith("Bearer ")) {
        authToken = authToken.substring(7);
    }
    String refreshedToken = jwtTokenUtil.refreshToken(authToken);
    if (refreshedToken == null) {
        return ResponseEntity.badRequest().body(null);
    } else {
        return ResponseEntity.ok(new JwtAuthenticationResponse(refreshedToken));
    }
}

前端在发现JWT即将过期时,可以调用这个接口来获取一个新的JWT。当然,你需要在JwtTokenUtil中实现refreshToken方法,用于生成新的JWT。

RuoYi-Vue的版本采用了Spring Security来进行权限认证。 Spring Security是一个强大且广泛使用的安全框架,它提供了一套全面的认证和授权机制,可以确保系统只有经过身份验证的用户才能访问受保护的资源。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [RuoYi-Vue-Oracle(RuoYi):基于SpringBoot的官方仓库,Spring SecurityJWT,Vue&Element的前替代分离...](https://download.csdn.net/download/weixin_42133918/15399484)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [RuoYi-Vue-Multi-Tenant:基于RuoYi-Vue扩展的多租户框架(SpringBoot,Spring SecurityJWT,Vue&Element...](https://download.csdn.net/download/weixin_42118160/16271786)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [ruoyi-vue-pro:基于SpringBoot,Spring SecurityJWT,Vue&Element的前一次分离权限管理系统](https://download.csdn.net/download/weixin_42166626/15607582)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值