首页 > 数据库 >安全认证框架【springSecurity】进行数据库效验开箱即用。

安全认证框架【springSecurity】进行数据库效验开箱即用。

时间:2025-01-19 15:29:47浏览次数:3  
标签:return ran springframework springSecurity 开箱 效验 org import public

流程:注册---加密密码---保存数据库---登录---授权---认证---效验数据库账号密码---生成token存redis---返回前端


第一步子模块引入依赖;版本号由父统一管理,这里有不理解的可以看我maven篇巩固一下。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>ran.youling</groupId>
        <artifactId>maven-object-ran</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>spring-security</artifactId>

    <!--引入依赖-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

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

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
        </dependency>

    </dependencies>
</project>

JwtUtils类,用于生成和解析token

package ran.youling.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 */
@Component
public class JwtUtils {

    /**
     * 签名密钥
     */
    private static final String signKey = "shaniuguilai";
    /**
     * 有效时间
     */
    private static final Long expire = 43200000L; //12小时过期

    /**
     * 生成JWT令牌
     *
     * @param claims JWT第二部分负载 payload 中存储的内容
     */
    public static String generateJwt(Map<String, Object> claims) {
        return Jwts.builder()
                .addClaims(claims)//自定义信息(有效载荷)
                .signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部)
                .setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间
                .compact();
    }

    /**
     * 解析JWT令牌
     *
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt) {
        return Jwts.parser()
                .setSigningKey(signKey)//指定签名密钥
                .parseClaimsJws(jwt)//指定令牌Token
                .getBody();
    }


//    public static void main(String[] args) {
//        Map<String, Object> map = new HashMap<>();
//        map.put("username", "zhangsan");
//        map.put("password", "123");
//        String jwt_token = JwtUtils.generateJwt(map);
//        System.out.println(jwt_token);
//
//        Claims claims = JwtUtils.parseJWT(jwt_token);
//        System.out.println(claims);
//    }
}

 Result<T>类 用于响应结果集

package ran.youling.util;

import lombok.Data;

@Data
public class Result<T> {

    // 状态码。
    private Integer code;

    // 信息。
    private String message;

    // 数据。
    private T data;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Result() {
    }

    public static <T> Result<T> build(Integer code, String message, T resultData) {

        Result<T> result = new Result<>();

        if (resultData != null) {
            result.setData(resultData);
        }

        result.setCode(code);
        result.setMessage(message);

        return result;
    }

    public static Result success() {
        Result result = new Result();
        result.setCode(ResultCodeEnum.SUCCESS.getResultCode());
        result.setMessage(ResultCodeEnum.SUCCESS.getResultMsg());
        return result;
    }

    public static <T> Result<T> success(T resultData) {
        return build(ResultCodeEnum.SUCCESS.getResultCode(), ResultCodeEnum.SUCCESS.getResultMsg(), resultData);

    }

    public static Result fail() {
        Result result = new Result();
        result.setCode(ResultCodeEnum.FAIL.getResultCode());
        result.setMessage(ResultCodeEnum.FAIL.getResultMsg());
        return result;
    }

    //失败的方法
    public static <T> Result<T> fail(T resultData) {
        return build(ResultCodeEnum.FAIL.getResultCode(), ResultCodeEnum.FAIL.getResultMsg(), resultData);
    }
}

ResultCodeEnum枚举类,用于Result<T>类必要参数

package ran.youling.util;

import lombok.Getter;

@Getter// Lombok 插件注解。
public enum ResultCodeEnum {
    // 自定义结果编码和结果信息。
    SUCCESS(200,"成功"),
    FAIL(400,"失败"),
    SERVICE_ERROR(401, "服务异常"),
    DATA_ERROR(402, "数据异常"),
    ILLEGAL_REQUEST(403, "非法请求"),
    REPEAT_SUBMIT(404, "重复提交"),
    LOGIN_AUTH(405, "未登陆"),
    PERMISSION(406, "没有权限"),
    // 自定义...
    ;

    // 结果编码。
    private Integer resultCode;

    // 结果信息。
    private String resultMsg;

    public Integer getResultCode() {
        return resultCode;
    }

    public String getResultMsg() {
        return resultMsg;
    }

    ResultCodeEnum(Integer resultCode, String resultMsg) {
        this.resultCode = resultCode;
        this.resultMsg = resultMsg;
    }
}

建表

 yml配置,nacos不是必须的(可选)

server:
  port: 9005

spring:
  application:
    name: spring-security
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # Nacos服务器的地址

  datasource: # 数据库配置应该放在这里
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/spring?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&autoReconnect=true&autoReconnectForPools=true
    username: root
    password: 666666

    #mybatisplus
  mybatis-plus:
    mapper-locations: classpath*:/mapper/*.xml # mapper映射文件
    lobal-config:
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启日至

RedisConfig配置类

package ran.youling.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
// 设置序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

创建UserController进行测试

@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/index")
    public String index() {
        return "欢迎访问首页";
    }

浏览器输入 http://localhost:9005/user/login 需进行认证

 

 分析:有些接口我们需要放行,比如登录接口、注册接口、首页等,否则前端页面无法访问

创建SecurityConfig配置类

package ran.youling.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


/***
 * NAME:Ran
 * TIME:2025/1/2
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启安全认证
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //    数据库密码加密解密的bean
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    //    重写权限认证的方法
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //    访问限制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()//关闭csrf攻击
                .authorizeHttpRequests().antMatchers("/login", "/enroll", "/index")//放行接口
                .authenticated();//其它接口需验证
    }

}

完善UserController

package ran.youling.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import ran.youling.pojo.User;
import ran.youling.service.impl.UserService;

import ran.youling.util.Result;

import java.util.Map;

/***
 * NAME:Ran
 * TIME:2025/1/12
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/index")
    public String index() {
        return "欢迎访问首页";
    }

    //    用户注册的方法
    @PostMapping("/enroll")
    public String userEnroll(@RequestBody User user) {
        return userService.enroll(user);
    }

    //    模拟用户登录
    @PostMapping("/login")
    public Result<Map<String, String>> login(@RequestBody User user) {
        return userService.login(user);
    }


}

UserService接口

package ran.youling.service.impl;


import com.baomidou.mybatisplus.extension.service.IService;
import ran.youling.pojo.User;
import ran.youling.util.Result;

import java.util.Map;

/**
* @author Administrator
* @description 针对表【user】的数据库操作Service
* @createDate 2025-01-12 09:43:59
*/
public interface UserService extends IService<User> {

//    处理用户登录接口
Result<Map<String,String>> login(User user);

    String enroll(User user);
}

 UserServiceImpl类,这是权限认证核心代码,注释已标清

package ran.youling.service.impl.impl;


import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import ran.youling.mapper.UserMapper;
import ran.youling.pojo.User;
import ran.youling.service.impl.UserService;

import ran.youling.util.JwtUtils;
import ran.youling.util.Result;
import ran.youling.vo.LoginUser;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * @author Administrator
 * @description 针对表【user】的数据库操作Service实现
 * @createDate 2025-01-12 09:43:59
 */

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {


    // 注入认证过滤器
    @Autowired
    private AuthenticationManager authenticationManager;
    //    redis
    @Autowired
    private StringRedisTemplate redisTemplate;
    //用于密码加密
    @Autowired
    private PasswordEncoder passwordEncoder;

//登录
    @Override
    public Result<Map<String, String>> login(User user) {
//把username和password等方法传递给security
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
//        注入认证过滤器 开始认证
        Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
// 输出结果:       UsernamePasswordAuthenticationToken [Principal=LoginUser(user=User(id=1, username=mengshujun, password=$2a$10$GysmSivtPRPPdT83CIgEGuusDd5soUIP275MdNjUmpuq.thg/YWxm, enabled=1)), Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[]]

        if (Objects.isNull(authentication)) {
            throw new UsernameNotFoundException("账号密码有误");
        }
//        使用构造传递给LoginUser实体类
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String userId = loginUser.getUser().getId().toString();//用于redisKey的唯一标识
//        封装token
        JSONObject from = JSONObject.from(loginUser);
        String token = JwtUtils.generateJwt(from);
//保存到redis并设置过期时间
        redisTemplate.opsForValue().set("login:" + userId, token, 2, TimeUnit.HOURS);//2,小时过期

        HashMap<String, String> stringHashMap = new HashMap<>();
        stringHashMap.put("token", token);
        return Result.success(stringHashMap);
    }


//注册
    @Override
    public String enroll(User user) {
//        调用passwordEncoder.encode进行密码加密,框架默认验证加密密码。
        String encode = passwordEncoder.encode(user.getPassword());
        User userOverride = new User();
        userOverride.setUsername(user.getUsername());
        userOverride.setPassword(encode);
        baseMapper.insert(userOverride);
        System.out.println("密码加密后:" + encode);
        return encode;
    }
}




我们需要LoginUser实体类(不是sql的实体类)实现UserDetails接口重写接口所有方法,用来接收security返回的数据

package ran.youling.vo;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import ran.youling.pojo.User;

import java.util.Collection;

/***
 * NAME:Ran
 * TIME:2025/1/12
 * 保存登录成功的数据
 */

@Data
@NoArgsConstructor

public class LoginUser implements UserDetails {
    private User user;

    public LoginUser(User user) {
        this.user = user;
    }

//    权限认证集合
    @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;
    }
}

创建UserDetailsService类 用来访问数据库

package ran.youling.service.impl.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import ran.youling.pojo.User;
import ran.youling.service.impl.UserService;
import ran.youling.vo.LoginUser;

import javax.annotation.Resource;
import java.util.Objects;

/***
 * NAME:Ran
 * TIME:2025/1/12
 */
@Service
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
    /**
     * 实现UserDetailsService重写loadUserByUsername方法 执行数据库操作
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */


    @Resource
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username", username);
        User user = userService.getOne(userQueryWrapper);
        if (Objects.isNull(user)) {
            throw new UsernameNotFoundException("暂无该用户");
        }
        return new LoginUser(user);
    }
}

测试

标签:return,ran,springframework,springSecurity,开箱,效验,org,import,public
From: https://blog.csdn.net/m0_74759856/article/details/145185157

相关文章

  • 【推荐】一款开源免费、开箱即用、功能强大的企业级后台管理系统
    项目介绍MineAdmin是一款基于Hyperf框架和Vue3+Vite5开发的开源免费、开箱即用、功能强大的前后端分离权限管理系统,自适应多终端。 项目用途系统可以用于网站管理后台、CMS(内容管理系统)、CRM(客户关系管理系统)、OA(办公自动化系统)、ERP(企业资源规划系统)等。项目特点......
  • 开箱你的 AI 语音女友「GitHub 热点速览」
    随着大模型API服务的不断丰富,开发者无需再依赖昂贵的硬件,也能轻松开发出拥有强大AI能力的应用。这不仅降低了技术门槛,也激发了极客们的创造力。就比如上周飙升1.5kStar的开源项目xiaozhi-esp32,仅用低成本的ESP32开发板和LLMAPI服务,就能制作出一个聪明有趣、......
  • 开箱即用!一款支持多个大语言模型服务的桌面客户端!
    大家好,我是Java陈序员。可以说现在AI给我们的生活、工作带来了极大的便利,各种大语言模型层出不穷,功能多样。今天,给大家介绍一款支持多模型服务的桌面客户端,开箱即用!关注微信公众号:【Java陈序员】,获取开源项目分享、AI副业分享、超200本经典计算机电子书籍等。项目介绍Ch......
  • springsecurity认证总结
    SpringSecurity登录认证整体流程以下是SpringSecurity登录认证流程的完整步骤,包括所有核心组件的参与和它们的作用:1.用户请求登录用户通过浏览器发送登录请求,通常为POST/login,并在请求体中携带用户名和密码。请求会被SpringSecurity拦截器链中的过滤器捕获,默认......
  • 【BUG排查记】HttpUtil和SpringSecurity结合的坑
    一、背景最近为了做微服务高可用和优化上线流程,我参与了一个微服务的改造开发。主要包括redis切换哨兵模式、接入高可用xxljob集群、配置和升级脚本优化。二、问题描述  项目改造提测后,测试发现一个依赖远程http调用的功能不可用三、问题分析  查看......
  • 【中州养老】《重点!!》 项目学习心得图解day06(一)权限认证-项目集成SpringSecurity(黑m程
    Day06权限认证-项目集成SpringSecurity文章目录Day06权限认证-项目集成SpringSecurity一、登录功能实现二、LoginServiceImpl的login方法思路三、将用户数据存入线程中四、自定义授权管理器一、登录功能实现二、LoginServiceImpl的login方法思路功能描述用户......
  • 安全框架SpringSecurity进阶【详解,附有图文+示例代码】
    文章目录十二.SpringSecurity进阶12.1认证流程12.2简单实现(无权限)思路分析准备工作导入依赖添加Redis相关配置Redis使用FastJson序列化RedisCache缓存Redis配置类响应类JWT工具类WebUtils工具类数据库准备实体类yml配置文件实现配置密码加密器12.3自定义登录接口12.......
  • SpringSecurity定制化开发(二)JWT登录
            JWT全称为JSONWebTokens,是一种开放标准(RFC7519),它定义了一种紧凑且独立的方式,用于在各方之间以JSON对象的形式安全地传输信息,非常适合用作用户登录的身份验证凭证。JWT的验证基于密钥,因此不需要在服务器端存储用户信息,这表明它是一种无状态的身份认证机制,可......
  • HarmonyOS 5.0 (Next)应用开发实战:使用ArkTS构建开箱即用的登录页面【HarmonyOS 5.0(Next
    HarmonyOS5.0(Next)应用开发实战:使用ArkTS构建开箱即用的登录页面【HarmonyOS5.0(Next)】一、HarmonyOS5.0美学与科技的完美融合在科技飞速发展的今天,每一个细微的创新都可能引领一场变革。华为,作为科技领域的领航者,再次以HarmonyOS5.0(Next)这一里程碑式的操作系统升级,向我们展......
  • 开箱即用的个人主页页面开发实战—基于HarmonyOS 5.0 (Next)和ArkTS的实现【HarmonyOS 5
    开箱即用的个人主页页面开发实战—基于HarmonyOS5.0(Next)和ArkTS的实现【HarmonyOS5.0(Next)】一、HarmonyOS5.0(Next)革新设计理念,打造和谐美学在科技日新月异的今天,操作系统作为智能设备的灵魂,正不断推动着数字生活的变革。华为,作为全球领先的科技企业,于近期正式推出了其原生鸿......