基于Ruoyi学习SpringSecurity的简单使用

简介

Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。
一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多,因为相比与SpringSecurity,Shiro的上手更加的简单。
一般Web应用的需要进行认证和授权。
认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户
授权:经过认证后判断当前用户是否有权限进行某个操作
而认证和授权也是SpringSecurity作为安全框架的核心功能。

准备工作

需要准备一个Springboot工程,完整的demo项目在这,欢迎指教。
下面给出项目的依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.0</version>
  </parent>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.5.3.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>

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

  </dependencies>

数据库文件可以在若依官网获取,本文的若依版本是3.8.7。不同版本数据库文件应该变化不大。
主要使用的是sys_user_role、sys_user、sys_role这三个表

创建一个test文件,验证mp和数据库配置是否正确。
Mapper 和 对应 Service 这里不做说明。

认证

Controller如下。业务写在了Controller内部,简化代码。

package com.ss.controller;

import com.ss.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import java.util.Objects;


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

    @PostMapping("/login")
    // @PreAuthorize("@ss.hasRole('admin', #user)") // 鉴权用,认证可以先注释掉
    @ResponseBody
    public String login(@RequestBody User user) {
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());

        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        if(Objects.isNull(authenticate)){
            throw new RuntimeException("用户名或密码错误");
        }
        return "111";
    }
}

User实体类

package com.ss.domain;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * 数据库中的 user
 */
@Data
@TableName("sys_user")
public class User {
    /**
     * 用户名
     */
    private String userName;
    /**
     * 用户密码
     */
    private String password;
    /**
     * 用户ID
     */
    private Long userId;
}

实现UserDetailsService接口的 loadUserByUsername通过用户名加载User对象方法。

package com.ss.security;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ss.domain.User;
import com.ss.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
public class LoginUserImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LambdaQueryWrapper<User> eq = new LambdaQueryWrapper<User>().eq(User::getUserName, username);
        User dbUser = userService.getOne(eq);

        //如果查询不到数据就通过抛出异常来给出提示
        if(Objects.isNull(dbUser)){
            throw new RuntimeException("用户名或密码错误");
        }
        return createLoginUser(dbUser);
    }

    private UserDetails createLoginUser(User dbUser) {
        return new SecurityUser(dbUser);
    }
}

因为 loadUserByUsername的返回值类型是 UserDetails 类型的,所以需要将 User类型转换成 UserDetails类型。
这里使用 SecurityUser类完成这个功能,使用它实现UserDetails 接口

package com.ss.security;

import com.ss.domain.User;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

@Data
@NoArgsConstructor
public class SecurityUser implements UserDetails {
    /**
     * 用户名 和 密码
     */
    private User user;


    public SecurityUser(User dbUser) {
        this.user = dbUser;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUserName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

最后是ss的配置类

package com.ss.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

// @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SsConfig extends WebSecurityConfigurerAdapter {


    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
                //关闭csrf
                http.csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问
                .antMatchers("/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();
        //把token校验过滤器添加到过滤器链中
//        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

}

授权

打开Controller的授权注解
添加实体类Role和UserRole
自定义授权类,这里使用了hasRole判断登录用户是否具有某个角色。

package com.ss.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ss.domain.Role;
import com.ss.domain.User;
import com.ss.domain.UserRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service("ss")
public class PermissionService {
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserRoleService userRoleService;
    /**
     * 判断用户是否拥有某个角色
     *
     * @param role 角色字符串
     * @return 用户是否具备某角色
     */
    public boolean hasRole(String role, User loginUser)
    {
        LambdaQueryWrapper<UserRole> eq = new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, loginUser.getUserId());
        UserRole one = userRoleService.getOne(eq);
        String roleId = one.getRoleId();
        if (roleId.isEmpty()) {
            return false;
        }
        LambdaQueryWrapper<Role> eq1 = new LambdaQueryWrapper<Role>().eq(Role::getRoleId, roleId);
        Role one1 = roleService.getOne(eq1);
        String roleKey = one1.getRoleKey();
        if (!Objects.isNull(roleKey) && roleKey.equals(role.trim())) {
            return true;
        }
        return false;
    }
}

测试

使用 HTTPClient进行测试

POST http://localhost:7878/login
Content-Type: application/json

{
  "userName": "ry",
  "password": "admin123",
  "userId": 2
}

POST http://localhost:7878/login
Content-Type: application/json

{
  "userName": "admin",
  "password": "admin123",
  "userId": 1
}

在这里插入图片描述

在这里插入图片描述

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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 Security,JWT,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 Security,JWT,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 Security,JWT,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、付费专栏及课程。

余额充值