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. Python之list函数

  2. 【原创】ARM平台内存和cache对xenomai实时性的影响

    目录 1. 问题概述 2. stress 内存压力原理 2. cache 因素 2.1 未加压 2.2 加压(cpu/io) 3. 内存管理因素 3.1 内存分配/释放 3.2 MMU拥塞 4 总结 ...

  3. 关于H5页面在微信浏览器中音视频播放的问题

    Android 上,因为各个软件使用的浏览器渲染引擎不一样,所以视频播放的效果差异也很大,这里主要以微信为主.微信使用的是腾讯浏览器自带的X5内核. 而iOS是不允许使用第三方浏览器内核的,就是Goo ...

  4. vue实现带logo的二维码/商品条形码/打印商品吊牌

    一.带logo的二维码 1.安装 npm install vue-qr --save 2.在页面或组件中使用 <template> <div id="qrcode" ...

  5. string.contains()

    public class test { public static void main(String[] args){ System.out.println("abcde".con ...

  6. 四、API Gateway相关------微服务构架设计模式

  7. yum安装Ceph指定Jewel版本

    前言 通过yum安装指定的rpm包,这个一般是 yum --showduplicates list ceph | expand ,然后去通过yum安装指定的版本即可,这个在hammer下是没有问题的, ...

  8. LeetCode-Python-删除链表解题思路

    给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点.   image.png 解题思路: 使用双指针,快指针与慢指针的间隔为n: 涉及到最后要删除慢指针的节点,为了方便,先开辟一个nod ...

  9. HECTF-5bit编码(Baudot Code)

    河北师大全国赛道比赛,第一次遇到这样的题,刚开始发现应该是一道摩斯密码的题,空格给去掉了,我在网上搜了半天, 列举全部的可能结果的工具,结果结果,啥也不是,感觉还是自己的能力不行吧,不过wp出来,感觉 ...

  10. powertool

    powertool简介 PowerTool 一款免费强大的进程管理器,支持进程强制结束,可以Unlock占用文件的进程,查看文件/文件夹被占用的情况,内核模块和驱动的查看和管理,进程模块的内存的dum ...