使用SpringSecurity Oauth2.0实现自定义鉴权中心
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实现自定义鉴权中心的更多相关文章
- Shiro(4)默认鉴权与自定义鉴权
=========默认鉴权======== 过滤链中定义: <!-- 过滤链定义 --> <property name="filterChainDefinitions&qu ...
- springboot+security整合(3)自定义鉴权
说明 springboot 版本 2.0.3源码地址:点击跳转 系列 springboot+security 整合(1) springboot+security 整合(2) springboot+se ...
- Spring Cloud Gateway + Jwt + Oauth2 实现网关的鉴权操作
Spring Cloud Gateway + Jwt + Oauth2 实现网关的鉴权操作 一.背景 二.需求 三.前置条件 四.项目结构 五.网关层代码的编写 1.引入jar包 2.自定义授权管理器 ...
- Spring Cloud注册中心Eureka设置访问权限并自定义鉴权页面
原文:https://blog.csdn.net/a823007573/article/details/88971496 使用Spring Security实现鉴权 1. 导入Spring Secur ...
- SpringSecurity Oauth2.0
1.用户认证分析 上面流程图描述了用户要操作的各个微服务,用户查看个人信息需要访问客户微服务,下单需要访问订单微服务,秒杀抢购商品需要访问秒杀微服务.每个服务都需要认证用户的身份,身份认证成功后,需要 ...
- spring-security oauth2.0简单集成
github地址:https://github.com/intfish123/oauth.git 需要2个服务,一个认证授权服务,一个资源服务 认证授权服务为客户端颁发令牌,资源服务用于客户端获取用户 ...
- 白话OAuth2用户认证及鉴权标准流程
一.OAuth2需求场景 在说明OAuth2需求及使用场景之前,需要先介绍一下OAuth2授权流程中的各种角色: 资源拥有者(User) - 指应用的用户 认证服务器 (Authorization S ...
- 【Spring Cloud & Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权
一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证授权.鉴权的逻辑,结合 ...
- SpringBoot整合SpringSecurityOauth2实现鉴权-动态权限
写在前面 思考:为什么需要鉴权呢? 系统开发好上线后,API接口会暴露在互联网上会存在一定的安全风险,例如:爬虫.恶意访问等.因此,我们需要对非开放API接口进行用户鉴权,鉴权通过之后再允许调用. 准 ...
随机推荐
- Java的Arrays.sort()方法到底用的什么排序算法
暂时网上看过很多JDK8中Arrays.sort的底层原理,有些说是插入排序,有些说是归并排序,也有说大于域值用计数排序法,否则就使用插入排序...其实不全对.让我们分析个究竟: 1 // Use Q ...
- Java_进程与线程
进Process&Thread 区别 进程 线程 根本区别 作为资源分配的单位 调度和执行的单位 开销 每个进程都有独立的代码和数据空间(进程上下文), 进程间的切换会有较大的开销 线程可以看 ...
- C++函数四( 具有默认参数值的函数)
在C++语言中,可以设置函数形参的默认值,在调用函数时,若明确给出了实参的值,则使用相应实参的值;若没有给出相应实参的值,则使用默认的值.这将为函数调用带来方便和灵活. [示例] #include&l ...
- Layui弹出层详解
今天空了学习一下弹出层 还是一步步展示把 首先,layer可以独立使用,也可以通过Layui模块化使用.我个人一直是用的模块化的 所以下面素有的都是基于模块化的. 引入好相关文件就可以开始啦 今天放 ...
- C++ 设计模式 1:概述
1 设计模式概述 1.1 定义 设计模式是在特定环境下人们解决某类重复出现问题的一套成功或有效的解决方案. 1.2 设计模式的种类 GoF 提出的设计模式有 23 个,包括: 创建型模式:如何创建对象 ...
- 性能问题eg
线上问题 ./pidstat -w Linux 3.6.5-Broadcom Linux ((none)) 03/21/20 _armv7l_ (1 CPU) 15:04:17 UID PID csw ...
- python <11>反射与异常
反射与异常加上类似于反射的文件的操作都将在下面以代码的形式体现. 一,反射 # _*_coding:utf-8_*_ # /usr/bin/env python3 # Author:book Miki ...
- mysql之binlog和各类日志介绍
1.错误日志 错误日志作用: 记录MySQL的启动.停止信息以及在MySQL运行过程中的错误信息. 参数log_error(默认开启) 修改后重启生效 log_error=[path/[file_n ...
- ipmi常用的命令行命令
前言 记录一些常用的命令行操作 命令 查询机器的电源状态 ipmitool -I lanplus -U admin -P admin -H 172.16.21.215 power status 硬重启 ...
- CSS属性(字体与文本属性)
1.字体属性 (1)font-family 把要对这个网站要设置的字体都写上,如果这个浏览器支持第一个字体,则会用,如果不支持则会尝试第二个,如果设置的字体系统都不支持则会使用系统默认的字体作为网站的 ...