SpringBoot2.7.4整合Oauth2

目录

1.先在数据库创建用户表t_user和客户端配置表oauth_client_details

2.插入表测试数据

3.打开pom.xml添加依赖

4.更改resources配置文件名称为application.yml,增加配置参数

5.在study下面新建各种包名,在resources下面新建mapper文件夹,编写相应的代码

6.在SpringBootStudyApplication添加注解@MapperScan扫描Mapper

7.打开类SpringBootStudyApplication,右键运行

8.打开postman调试

9.下载地址


1.先在数据库创建用户表t_user和客户端配置表oauth_client_details

CREATE TABLE `t_user` (

`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',

`username` varchar(255) NOT NULL COMMENT '用户名',

`password` varchar(255) NOT NULL COMMENT '密码',

`rolename` varchar(255) DEFAULT NULL COMMENT '角色,逗号隔开',

`enabled` int(11) DEFAULT NULL COMMENT '是否启用',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `oauth_client_details` (

`client_id` varchar(128) NOT NULL COMMENT '客户端ID',

`resource_ids` varchar(256) DEFAULT NULL COMMENT '资源ID集合,多个用逗号分隔',

`client_secret` varchar(256) DEFAULT NULL COMMENT '客户端密钥',

`scope` varchar(256) DEFAULT NULL COMMENT '权限范围',

`authorized_grant_types` varchar(256) DEFAULT NULL COMMENT '授权类型',

`web_server_redirect_uri` varchar(256) DEFAULT NULL COMMENT '重定向URI',

`authorities` varchar(256) DEFAULT NULL COMMENT '权限',

`access_token_validity` int(11) DEFAULT NULL COMMENT '访问令牌有效期(秒)',

`refresh_token_validity` int(11) DEFAULT NULL COMMENT '刷新令牌有效期(秒)',

`additional_information` varchar(4096) DEFAULT NULL COMMENT '附加信息',

`autoapprove` varchar(256) DEFAULT NULL COMMENT '是否自动授权',

PRIMARY KEY (`client_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.插入表测试数据

#第一条密码是123456,第二条是666666

INSERT INTO `springboot_study`.`t_user` (`id`, `username`, `password`, `rolename`, `enabled`) VALUES (1, 'saas', '2a10$prTTUfRO8cBfpZKxjbJ6XO2oJ5ILCeocEgtYomhK9BU8ELTFk90Da', 'ROLE_USER', 1);

INSERT INTO `springboot_study`.`t_user` (`id`, `username`, `password`, `rolename`, `enabled`) VALUES (2, 'admin', '2a10$b5c2KKDQSo.u8MEDDrSbh.Z0ZsbVU1.2ZNapGRgTvLw0fLMeqFIA.', 'ROLE_ADMIN', 1);

INSERT INTO oauth_client_details (

client_id,

resource_ids,

client_secret,

scope,

authorized_grant_types,

web_server_redirect_uri,

authorities,

access_token_validity,

refresh_token_validity,

additional_information,

autoapprove

) VALUES (

'client1', -- 客户端ID

'oauth2-resource', -- 资源ID(需与资源服务器配置的resourceId一致)

'2a10$LQx7R87M8J2l3Y8X9Z0A5eOeFgHhIjKlMnOpQrStUvWxYz1234567', -- 客户端密钥(BCrypt加密,原文:secret123)

'read,write', -- 权限范围

'password,refresh_token,client_credentials', -- 支持的授权类型(密码模式、刷新令牌、客户端凭证模式)

null, -- 重定向URI(授权码模式需配置)

null, -- 客户端权限

3600, -- 访问令牌有效期(秒)

86400, -- 刷新令牌有效期(秒)

'{}', -- 附加信息

'true' -- 是否自动授权

);

3.打开pom.xml添加依赖

java 复制代码
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <!--mysql数据库驱动程序-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

        <!--支持通过jdbc连接数据库-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <!-- MyBatis-Plus 核心(与 2.7.4 兼容) -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

        <!--mybatis-plus代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!--mybatis-plus代码生成器依赖的模板引擎-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.2</version>
        </dependency>

        <!-- Lombok(简化实体类) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--安全框架spring security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!--OAuth2授权服务器 -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.6.8</version>
        </dependency>

        <!--jwt支持-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.1.1.RELEASE</version>
        </dependency>

        <!--jwt 核心库-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

4.更改resources配置文件名称为application.yml,增加配置参数

java 复制代码
server:
  port: 8888 #指定启动的端口
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver #数据库驱动
    url: jdbc:mysql://localhost:3306/springboot_study?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&useSSL=false #数据库的url
    username: root #数据库的用户名
    password: 123456 #数据库的密码
    type: com.alibaba.druid.pool.DruidDataSource #指定数据源
    druid:
      initial-size: 5                                       # 初始化大小
      min-idle: 10                                          # 最小连接数
      max-active: 20                                        # 最大连接数
      max-wait: 60000                                       # 获取连接时的最大等待时间
      min-evictable-idle-time-millis: 300000                # 一个连接在池中最小生存的时间,单位是毫秒
      time-between-eviction-runs-millis: 60000              # 多久才进行一次检测需要关闭的空闲连接,单位是毫秒
      filters: stat,wall                                    # 配置扩展插件:stat-监控统计,log4j-日志,wall-防火墙(防止SQL注入),去掉后,监控界面的sql无法统计
      validation-query: SELECT 1                            # 检测连接是否有效的 SQL语句,为空时以下三个配置均无效
      test-on-borrow: true                                  # 申请连接时执行validationQuery检测连接是否有效,默认true,开启后会降低性能
      test-on-return: true                                  # 归还连接时执行validationQuery检测连接是否有效,默认false,开启后会降低性能
      test-while-idle: true                                 # 申请连接时如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效,默认false,建议开启,不影响性能
      stat-view-servlet:
        enabled: true                                       # 是否开启 StatViewServlet
        allow: 127.0.0.1                                    # 访问监控页面的白名单,默认127.0.0.1(多个以逗号隔开)
        deny: 192.168.0.1                                   # 访问监控页面的黑名单(存在共同时,deny优先于allow)
        login-username: admin                               # 访问监控页面的登陆账号
        login-password: 123456                               # 访问监控页面的登陆密码
      filter:
        stat:
          enabled: true                                     # 是否开启 FilterStat,默认true
          log-slow-sql: true                                # 是否开启 慢SQL 记录,默认false
          slow-sql-millis: 5000                             # 慢 SQL 的标准,默认 3000,单位:毫秒
          merge-sql: false                                  # 合并多个连接池的监控数据,默认false

# jwt配置
jwt:
  key: mySecretKey123  # jwt签名密钥(生产环境需使用复杂密钥)
  access-token-expire: 3600  # 访问令牌有效期(秒)
  refresh-token-expire: 86400  # 刷新令牌有效期(秒)

oauth:
  client-id: client1  # 与数据库中oauth_client_details的client_id一致

5.在study下面新建各种包名,在resources下面新建mapper文件夹,编写相应的代码

entity层

java 复制代码
package com.saas.study.entity;

import com.baomidou.mybatisplus.annotation.TableName;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * <p>
 * 
 * </p>
 *
 * @author yy
 * @since 2025-11-17
 */
@Getter
@Setter
@TableName("t_user")
public class User implements UserDetails {


    /**
     * 主键id
     */
    private Long id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 是否启用
     */
    private Boolean enabled;

    /**
     * 角色集合 ROLE_ADMIN、ROLE_USER
     */
    private List<String> roles;

    /**
     * 角色字段(逗号隔开)
     */
    private String roleName;

    // 核心方法:返回用户的权限(Spring Security 要求角色以 ROLE_ 开头)
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<String> roleList= Arrays.stream(roleName.split(","))
                .collect(Collectors.toList());
        return roleList.stream()
                .map(role -> new SimpleGrantedAuthority(role))
                .collect(Collectors.toList());
    }

    // 账户是否未过期(默认 true)
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    // 账户是否未锁定(默认 true)
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    // 凭证是否未过期(默认 true)
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    // 账户是否启用(关联数据库 enabled 字段)
    @Override
    public boolean isEnabled() {
        return enabled;
    }


}

controller层

java 复制代码
package com.saas.study.controller;


import com.saas.study.entity.User;
import com.saas.study.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.web.bind.annotation.*;

import org.springframework.stereotype.Controller;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author yy
 * @since 2025-11-17
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @Autowired
    private AuthenticationManager authenticationManager; //认证管理器

    @Autowired
    private ClientDetailsService clientDetailsService; //客户端信息服务

    @Autowired
    private AuthorizationServerTokenServices tokenServices; //令牌服务

    @Value("${oauth.client-id:client1}") //配置文件中配置客户端ID
    private String clientId;

    //登录接口
    @PostMapping("/login")
    public String login(@RequestParam String username,
                        @RequestParam String password,
                        @RequestParam(required = false) String code) {
        String result = null;
        try {
            //手动触发用户名密码认证(会调用 UserDetailsService 校验用户)
            UsernamePasswordAuthenticationToken authToken =
                    new UsernamePasswordAuthenticationToken(username, password);
            Authentication authentication = authenticationManager.authenticate(authToken);

            //构建客户端信息(模拟客户端认证)
            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
            Map<String, String> requestParameters = new HashMap<>();
            OAuth2Request oAuth2Request = new OAuth2Request(
                    requestParameters, clientId, clientDetails.getAuthorities(),
                    true, clientDetails.getScope(), clientDetails.getResourceIds(),
                    null, null, null
            );

            //构建 OAuth2 认证对象(用户认证 + 客户端认证)
            OAuth2Authentication oAuth2Authentication =
                    new OAuth2Authentication(oAuth2Request, authentication);

            //生成令牌(调用授权服务器的令牌服务)
            OAuth2AccessToken accessToken = tokenServices.createAccessToken(oAuth2Authentication);

            //返回结果(这里手动拼接,自行更改)
            StringBuilder resultBuilder = new StringBuilder();
            resultBuilder.append("{")
                    .append("\"code\":200,")
                    .append("\"message\":\"登录成功\",")
                    .append("\"data\":{")
                    .append("\"access_token\":\"").append(accessToken.getValue()).append("\",")
                    .append("\"token_type\":\"").append(accessToken.getTokenType()).append("\",")
                    .append("\"expires_in\":").append(accessToken.getExpiresIn()).append(",")
                    .append("\"scope\":[\"").append(String.join("\",\"", accessToken.getScope())).append("\"]");

            //可选:添加刷新令牌
            if (accessToken.getRefreshToken() != null) {
                resultBuilder.append(",\"refresh_token\":\"").append(accessToken.getRefreshToken().getValue()).append("\"");
            }

            resultBuilder.append("}}");

            result = resultBuilder.toString();
        } catch (Exception e) {
            //认证失败
            result = "{\"code\":500,\"message\":"+e.getMessage()+"}";
        }
        return result;
    }

    //获取所有的用户信息
    @GetMapping("/base/getUserInfoList")
    public List<User> getUserInfoList(){
        return userService.getUserInfoList();
    }


    //调整登录接口
    @GetMapping("/tologin")
    public String userInfo() {
        return "去登录";
    }
}
复制代码
service层
java 复制代码
package com.saas.study.service;

import com.saas.study.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author yy
 * @since 2025-11-17
 */
public interface UserService extends IService<User> {

    List<User> getUserInfoList();
}
java 复制代码
package com.saas.study.service.impl;

import com.saas.study.entity.User;
import com.saas.study.mapper.UserMapper;
import com.saas.study.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author yy
 * @since 2025-11-17
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public List<User> getUserInfoList() {
        return userMapper.getUserInfoList();
    }
}
java 复制代码
package com.saas.study.service.impl;

import com.saas.study.entity.User;
import com.saas.study.mapper.UserMapper;
import org.apache.commons.lang3.StringUtils;
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.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    // Spring Security 会自动调用此方法,根据用户名查询用户信息
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询用户
        User user = userMapper.selectUserByUsername(username);
        if (user == null || StringUtils.isBlank(user.getRoleName())) {
            throw new UsernameNotFoundException("用户名不存在");
        }

        //设置角色
        List<String> roles = Arrays.stream(user.getRoleName().split(","))
                .collect(Collectors.toList());

        user.setRoles(roles);

        return user;
    }
}

mapper层

java 复制代码
package com.saas.study.mapper;

import com.saas.study.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import java.util.List;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author yy
 * @since 2025-11-17
 */
public interface UserMapper extends BaseMapper<User> {

    List<User> getUserInfoList();

    User selectUserByUsername(String username);
}
java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.saas.study.mapper.UserMapper">

    <select id="getUserInfoList" resultType="com.saas.study.entity.User">
        select id,username,password,rolename from t_user
    </select>

    <select id="selectUserByUsername" resultType="com.saas.study.entity.User">
        SELECT id, username, password, enabled,rolename
        FROM t_user
        WHERE username = #{username}
    </select>
</mapper>

component层

java 复制代码
package com.saas.study.component;

import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 自定义权限不足处理器(403)
 */
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException accessDeniedException) throws IOException {
        // 前后端分离:返回JSON
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(HttpStatus.FORBIDDEN.value()); // 403状态码

        PrintWriter out = response.getWriter();
        // 自定义JSON响应
        out.write("{\"code\":403,\"message\":\"权限不足,无法访问该资源\",\"detail\":\"" + accessDeniedException.getMessage() + "\"}");
        out.flush();
        out.close();
    }
}
java 复制代码
package com.saas.study.component;

import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 自定义认证失败处理器(401):处理令牌无效、过期、未携带令牌等场景
 */
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException) throws IOException {
        //前后端分离:返回JSON
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(HttpStatus.UNAUTHORIZED.value()); // 401状态码

        PrintWriter out = response.getWriter();
        //自定义JSON响应
        String message;
        String detail = authException.getMessage();

        // 区分异常场景,返回更精准的提示
        if ("Full authentication is required to access this resource".equals(detail)) {
            message = "请先登录"; // 未登录场景
        } else if (detail.contains("expired")) {
            message = "登录已过期,请重新登录"; // 令牌过期
        } else if (detail.contains("invalid")) {
            message = "令牌无效"; // 令牌错误
        } else {
            message = "认证失败,无法访问资源"; // 其他认证异常
        }

        // 拼接JSON响应
        out.write("{\"code\":401,\"message\":\"" + message + "\",\"detail\":\"" + detail + "\"}");out.flush();
        out.close();
    }
}

config层

java 复制代码
package com.saas.study.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;

/**
 * 认证服务配置类
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private DataSource dataSource;

    @Value("${jwt.key}")
    private String secretKey;

    @Value("${jwt.access-token-expire}")
    private int accessTokenExpire;

    //配置客户端信息来源(数据库)
    @Bean
    public JdbcClientDetailsService customClientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    //配置jwt令牌转换器
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        //根据需要自定义jwt生成和解析
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(secretKey);
        return converter;
    }

    //配置令牌存储(jwt)
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    //配置令牌服务(关键:关联TokenEnhancer/AccessTokenConverter)
    @Bean
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setClientDetailsService(customClientDetailsService());
        services.setSupportRefreshToken(true);
        services.setTokenStore(tokenStore());
        services.setTokenEnhancer(accessTokenConverter()); // 关联JWT转换器
        services.setAccessTokenValiditySeconds(accessTokenExpire);
        return services;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(customClientDetailsService()); //使用数据库客户端信息
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenStore(tokenStore())
                .accessTokenConverter(accessTokenConverter())
                .tokenServices(tokenServices()); //关联自定义令牌服务
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                .allowFormAuthenticationForClients() //允许客户端表单认证
                .tokenKeyAccess("permitAll()") //公开令牌密钥端点
                .checkTokenAccess("isAuthenticated()"); //验证令牌需认证
    }
}
java 复制代码
package com.saas.study.config;

import com.saas.study.component.CustomAccessDeniedHandler;
import com.saas.study.component.CustomAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;

/**
 * 资源服务器配置:整合所有异常处理器
 */
@Configuration
@EnableAuthorizationServer
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    // 注入自定义处理器
    @Autowired
    private CustomAccessDeniedHandler customAccessDeniedHandler;
    @Autowired
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    /**
     * 配置资源服务器安全规则(异常处理)
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
                // 认证失败(401):令牌无效/过期等
                .authenticationEntryPoint(customAuthenticationEntryPoint)
                // 权限不足(403):已认证但无权限
                .accessDeniedHandler(customAccessDeniedHandler);
    }

    /**
     * 配置接口访问权限规则
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable() //前后端分离关闭CSRF
                .authorizeRequests()
                //公开接口(无需认证)
                .antMatchers("/user/tologin","/user/login").permitAll()
                //管理员接口(需ADMIN角色)
                .antMatchers("/admin/base/**").hasRole("ADMIN")
                //用户接口(USER角色,支持多个)
                .antMatchers("/user/base/**").hasAnyRole( "USER")
                //其他所有接口需认证
                .anyRequest().authenticated();
    }
}
java 复制代码
package com.saas.study.config;

import com.saas.study.component.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

/**
 * security安全配置类
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    //密码加密器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //认证管理器
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/oauth/**").permitAll() //开放OAuth相关端点
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll(); //允许表单登录(可选)
    }
}

6.在SpringBootStudyApplication添加注解@MapperScan扫描Mapper

java 复制代码
package com.saas.study;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.saas.study.**.mapper")
public class SpringbootStudyApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootStudyApplication.class, args);
    }

}

7.打开类SpringBootStudyApplication,右键运行

8.打开postman调试

①.访问http://localhost:8888/user/login?username=saas&password=123456进行登录

②.访问http://localhost:8888/user/base/getUserInfoList 获取所有用户信息(请求头添加字段Authorization,值是Bearer+空格+第一步获取的access_token)

③.模拟当登录用户改成admin,再次访问获取所有用户接口时候的情况

9.下载地址

百度云:https://pan.baidu.com/s/1-N3CfbmhQZ5X8agKnGQXFw 提取码: nm7n

相关推荐
ZHOUZAIHUI1 小时前
WSL(Ubuntu24.04) 安装PostgreSQL
开发语言·后端·scala
欧阳x天1 小时前
C++入门(二)
开发语言·c++
CappuccinoRose1 小时前
MATLAB学习文档(二十八)
开发语言·学习·算法·matlab
爱敲代码的loopy1 小时前
MATLAB函数全称解析:旋转翻转找数字
开发语言·matlab
月屯2 小时前
后端go完成文档分享链接功能
开发语言·后端·golang
Franciz小测测2 小时前
Python连接RabbitMQ三大方案全解析
开发语言·后端·ruby
代码雕刻家3 小时前
C语言的左对齐符号-
c语言·开发语言
小肖爱笑不爱笑3 小时前
2025/11/19 网络编程
java·运维·服务器·开发语言·计算机网络
郑州光合科技余经理3 小时前
开发指南:海外版外卖跑腿系统源码解析与定制
java·开发语言·mysql·spring cloud·uni-app·php·深度优先