Oauth2.0是什么不在赘述,本文主要介绍如何使用SpringSecurity Oauth2.0实现自定义的用户校验

1.鉴权中心服务

首先,列举一下我们需要用到的依赖,本文采用的是数据库保存用户信息redis保存token的方式。

pom依赖:

       ---- security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
---- oauth2.0依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
----- redis依赖用于存储token

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
----- mybatisPlus用于操作数据库

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

------mysql 驱动
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>

添加完引用后,我们需要配置Oauth鉴权中的两大服务:鉴权服务和资源服务

一、鉴权服务

鉴权服务中我们需要配置存储token用的redis,用于从DB中拉取Client信息的服务类,用于进行Token生产和维护的服务类,最后还要外露获取token的节点和进行token校验的节点

在开始以前首先我们要搞清SpringSecurity中的一些基本类的作用和概念,这里贴出一篇不错的文章: https://www.cnkirito.moe/spring-security-1/

1.首先我们要定义我们的用户实体类,必须要实现UserDetails接口,我们的实体类中仅仅简单的定义了用户名和密码,剩下的全部实现自UserDetails接口

public class User implements UserDetails {

    @TableId(type = IdType.AUTO)
private int id; private String username; private String password; private boolean isEnabled; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public void setUsername(String username) {
this.username = username;
} public void setPassword(String password) {
this.password = password;
} public void setEnabled(boolean enabled) {
isEnabled = enabled;
} @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
} @Override
public String getPassword() {
return this.password;
} @Override
public String getUsername() {
return this.username;
} @Override
public boolean isAccountNonExpired() {
return true;
} @Override
public boolean isAccountNonLocked() {
return true;
} @Override
public boolean isCredentialsNonExpired() {
return true;
} @Override
public boolean isEnabled() {
return this.isEnabled;
}
}

数据库脚本:

CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`is_account_non_expired` tinyint(4) NOT NULL,
`is_account_non_locked` tinyint(4) DEFAULT NULL,
`is_credentials_non_expired` tinyint(4) NOT NULL,
`is_enabled` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

2.自定义自己的用户查找服务类,必须集成UserDetaailsService接口且实现loadUserByUsername接口,届时spring框架会通过这个方法内的自定义逻辑找到你想要的用户

public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
private UserMapper userMapper; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user= userMapper.queryUserByUsername(username);
if (user==null){
throw new UsernameNotFoundException("user no found");
}
return user;
}
}

userMapper:就是一个非常基础的数据库查询语句

<mapper namespace="com.example.oauth2.mapper.UserMapper">
<select id="queryUserByUsername" resultType="com.example.oauth2.entity.User">
select * from t_user where user_name=#{username}
</select>
</mapper>

3.配置鉴权服务器,我们需要把redis和上面自定义的用户查询配置到鉴权服务里

Configuration
//开启鉴权服务器
@EnableAuthorizationServer
public class AuthoriztionServerConfiguration extends AuthorizationServerConfigurerAdapter { //全局SpringSecurity AuthenticationManager 需要在SpringSecurity中注入,下文会写到
@Autowired
private AuthenticationManager authenticationManager; //redis工厂会自动配置
@Autowired
private RedisConnectionFactory redisConnectionFactory; //数据源会自动配置
@Autowired
private DataSource dataSource; //我们自定义的UserDetailsService实现类
@Autowired
private UserDetailsService userDetailsService; //全局加密编码类
@Autowired
private BCryptPasswordEncoder passwordEncoder; /**
* 用来配置令牌端点的安全约束
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
//允许所有用户访问 /oauth/token节点来获取token
.tokenKeyAccess("permitAll()")
//允许已经鉴权通过的用户访问 /oauth/check_token节点
.checkTokenAccess("isAuthenticated()")
// 允许表单认证
.allowFormAuthenticationForClients();
} /**
* 定义客户端详细信息的配置器
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 从DB中拉取client信息
clients.withClientDetails(jdbcClientDetailsService());
} /**
*配置鉴权服务终结点配置
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception{
endpoints.authenticationManager(this.authenticationManager)
//配置允许访问的http方式
.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST)
//用于配置自定义的用户校验服务
.userDetailsService(userDetailsService)
//配置自定义的token保存地址
.tokenStore(tokenStore())
//用于配置自定义的token维护服务
.tokenServices(tokenServices());
} /**
* 配置使用redis来存储token
* @return
*/
@Bean
public TokenStore tokenStore(){
return new RedisTokenStore(redisConnectionFactory);
} /**
* 配置通过数据的方式去读取client信息
* @return
*/
@Bean
public ClientDetailsService jdbcClientDetailsService(){
JdbcClientDetailsService jdbcClientDetailsService=new JdbcClientDetailsService(dataSource);
jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);
return jdbcClientDetailsService;
} /**
* 自定义token的生产和过期机制
* @return
*/
@Bean
public DefaultTokenServices tokenServices(){
DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
defaultTokenServices.setAccessTokenValiditySeconds((int)TimeUnit.HOURS.toSeconds(4));
defaultTokenServices.setRefreshTokenValiditySeconds((int)TimeUnit.HOURS.toSeconds(2));
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
} /**
* 指定全局加密方式
* @return
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}

同时我们需要在数据库中建一张client的信息表SQL脚本:

CREATE TABLE `oauth_client_details` (
`client_id` varchar(256) NOT NULL,
`resource_ids` varchar(256) DEFAULT NULL,
`client_secret` varchar(256) DEFAULT NULL,
`scope` varchar(256) DEFAULT NULL,
`authorized_grant_types` varchar(256) DEFAULT NULL,
`web_server_redirect_uri` varchar(256) DEFAULT NULL,
`authorities` varchar(256) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(4096) DEFAULT NULL,
`autoapprove` varchar(256) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

完成以上步骤后鉴权服务器就配置完成了,记下来我们需要配置资源服务器

2.资源服务器

资源服务器配置相对简单,我们只需要暴露出可供用户使用的节点即可

@Configuration
//开启资源服务
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override
public void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
//暴露出/oauth/**的所有接口
.antMatchers("/oauth/**")
.permitAll()
.anyRequest()
.authenticated();
}
}

配置完资源服务之后最关键的一步来了,我们需要自定义自己的用户校验

3.自定义用户校验

SpringSecurity中所有的校验都是通过AuthenticationProvider来实现的,所以我们需要实现自己的Provider注入到用于校验的Provider列表中去

@Component
public class MyAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { //前面我们自己实现的读取用户信息的实现类
@Autowired
private UserDetailsService userDetailsService; //全局加密方式
@Autowired
private BCryptPasswordEncoder passwordEncoder; //对表单传入的用户信息和线程上下文中的用户进行比对判断是否是正确的用户
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
//数据库中的密码
String authPassword = userDetails.getPassword();
//上下文中的密码
String tokenPassword = (String) usernamePasswordAuthenticationToken.getCredentials();
boolean isPass = passwordEncoder.matches(tokenPassword, authPassword);
if (isPass) {
return;
}
throw new AuthenticationServiceException("password.wrong");
} //根据表单中传入的用户名从数据库中获取用户信息
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
UserDetails user = userDetailsService.loadUserByUsername(username);
if (user == null) {
throw new AuthenticationServiceException("未找到用户");
}
return user;
}
}

把我们自定的provider注入进去

@Configuration
public class GlobalAuthenticationConfig extends GlobalAuthenticationConfigurerAdapter { @Autowired
private MyAuthenticationProvider authenticationProvider; @Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider( authenticationProvider); }
}

最后我们需要配置一下SpringSecurity

4.配置SpringSecurity

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private MyAuthenticationProvider authenticationProvider; /**
* 注入用户信息服务
* @return 用户信息服务对象
*/
@Bean
@Override
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
} @Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
} @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/oauth/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
}

然后配置文件中配好数据库和redis信息

server:
port: 5000
spring:
datasource:
url: xxx
username: xxx
password: xxx
driver-class-name: com.mysql.jdbc.Driver
redis:
host: xxx
port: 6379
database: 7
password: xxx mybatis-plus:
mapper-locations: classpath:/mapper/*Mapper.xml
configuration:
map-underscore-to-camel-case: true
logging:
level:
ROOT: debug

在启动之前我们需要先在数据库中添加一个client和一个用户

@SpringBootTest
class Oauth2ApplicationTests { @Autowired
JdbcClientDetailsService jdbcClientDetailsService; @Autowired
private BCryptPasswordEncoder passwordEncoder; @Test
void contextLoads() {
//通过我们在鉴权服务中注入的JdbcClientDetailsService来插入一条Client信息
BaseClientDetails baseClientDetails = new BaseClientDetails();
baseClientDetails.setClientId("client");
baseClientDetails.setClientSecret(passwordEncoder.encode("secret"));
List<String> grantTypes = new ArrayList<>();
grantTypes.add("password");
baseClientDetails.setAuthorizedGrantTypes(grantTypes);
jdbcClientDetailsService.addClientDetails(baseClientDetails);
//用指定的密码生成器搞一个密码一会手动填到库里这里就不写sql 了
String str = passwordEncoder.encode("666");
System.out.println("终于他妈的完事了");/**/
} }

注意给scope填个select上面忘记写了

启动项目访问获取通通token的接口,完美。

使用SpringSecurity Oauth2.0实现自定义鉴权中心的更多相关文章

  1. Shiro(4)默认鉴权与自定义鉴权

    =========默认鉴权======== 过滤链中定义: <!-- 过滤链定义 --> <property name="filterChainDefinitions&qu ...

  2. springboot+security整合(3)自定义鉴权

    说明 springboot 版本 2.0.3源码地址:点击跳转 系列 springboot+security 整合(1) springboot+security 整合(2) springboot+se ...

  3. Spring Cloud Gateway + Jwt + Oauth2 实现网关的鉴权操作

    Spring Cloud Gateway + Jwt + Oauth2 实现网关的鉴权操作 一.背景 二.需求 三.前置条件 四.项目结构 五.网关层代码的编写 1.引入jar包 2.自定义授权管理器 ...

  4. Spring Cloud注册中心Eureka设置访问权限并自定义鉴权页面

    原文:https://blog.csdn.net/a823007573/article/details/88971496 使用Spring Security实现鉴权 1. 导入Spring Secur ...

  5. SpringSecurity Oauth2.0

    1.用户认证分析 上面流程图描述了用户要操作的各个微服务,用户查看个人信息需要访问客户微服务,下单需要访问订单微服务,秒杀抢购商品需要访问秒杀微服务.每个服务都需要认证用户的身份,身份认证成功后,需要 ...

  6. spring-security oauth2.0简单集成

    github地址:https://github.com/intfish123/oauth.git 需要2个服务,一个认证授权服务,一个资源服务 认证授权服务为客户端颁发令牌,资源服务用于客户端获取用户 ...

  7. 白话OAuth2用户认证及鉴权标准流程

    一.OAuth2需求场景 在说明OAuth2需求及使用场景之前,需要先介绍一下OAuth2授权流程中的各种角色: 资源拥有者(User) - 指应用的用户 认证服务器 (Authorization S ...

  8. 【Spring Cloud & Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证授权.鉴权的逻辑,结合 ...

  9. SpringBoot整合SpringSecurityOauth2实现鉴权-动态权限

    写在前面 思考:为什么需要鉴权呢? 系统开发好上线后,API接口会暴露在互联网上会存在一定的安全风险,例如:爬虫.恶意访问等.因此,我们需要对非开放API接口进行用户鉴权,鉴权通过之后再允许调用. 准 ...

随机推荐

  1. Docker系列01—Docker 基础入门

    一.初识Docker和容器 1.1 什么是docker 容纳其他物品的工具,可以部分或完全封闭,被用于容纳.存储.运输物品.物体可以被放置在容器中,而容器则可以保护内容物. 容器? 容器就是在隔离的环 ...

  2. 烧录时发生:permission denied:'/dev/ttyUSB0'问题的解决

    在执行make flash的过程中出现错误: 解决办法: sudo chmod -R 777 /dev/ttyUSB0 这种设置在下次使用的,又会出现这种问题,还要重新设置 永久性的设置可以使用下面这 ...

  3. tp3.2 前端截取字符串

    在Common目录中建立 function.php <?php function subtext($text, $length) { if(mb_strlen($text, 'utf8') &g ...

  4. sendfile zero-copy

    传统read/write进行网络文件传输过程当中,文件数据实际上经过四次copy操作: 硬盘->内核buf->用户buf->socket相关缓冲区->协议引擎 而sendfil ...

  5. Gromacs分子动力学模拟流程概述

    Gromacs分子动力学模拟主要可以分为以下几个步骤,不同的体系步骤可能略有不同. 在开始之前,先简单了解一下预平衡: 分子动力学模拟的最终目的是对体系进行抽样,然后计算体系的能量,各种化学键,成分分 ...

  6. tp6.0.x 反序列化漏洞

    tp6 反序列化漏洞复现 环境 tp6.0 apache php7.3 漏洞分析 反序列化漏洞需要存在 unserialize() 作为触发条件,修改入口文件 app/controller/Index ...

  7. c++实现split

    #include<iostream> #include<vector> #include<algorithm> #include<string> usi ...

  8. UNP第11章——名字与地址转换

    1.域名系统 程序中只使用主机名和服务名的好处是,如果IP或端口变化,只需要改变映射关系,不需要重新编译程序. 1.1 资源记录 DNS的条目为资源记录,有用的项如下: A IPv4地址 AAAA I ...

  9. java1.8安装及环境变量配置

    一.前言 虽然jdk1.9版本已经问世,但是许多其他的配套设施并不一定支持jdk1.9版本,所以这里仅带领你配置jdk1.8.而jdk1.9的操作也几乎是相同的. 本教程适用于windows10 64 ...

  10. 腾讯云容器服务 TKE 推出新一代零损耗容器网络

    随着容器技术的发展成熟,越来越多的组件迁移到容器,在技术迁移过程中,数据库,游戏,AI 这些组件对容器网络性能(时延,吞吐,稳定性)提出了更高的要求.为了得到更优的时延和吞吐表现,各大云厂商都在致力于 ...