上个星期一个朋友请求帮忙,让我搭建一个分布式授权中心的微服务,之前我也没搭建过,在网上撸了几天前辈们写的技术博客,搞出个模型,分享给大家:

前辈们博客地址:

OAuth2.0 原理:https://blog.csdn.net/tclzsn7456/article/details/79550249

JWT 原理:https://baijiahao.baidu.com/s?id=1608021814182894637&wfr=spider&for=pc

以下是代码,我这个是通过密码模式进行认证的:

0.目录结构:

1.引入依赖:

 <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.7.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>

2.编写配置类 AuthorizationParam 并在application.yml 中填写具体参数:

@Data
@ConfigurationProperties(prefix = "authorizationparam")
public class AuthorizationParam {
private String clientId; //客户端id
private String secret; //(可信客户端需要)客户端密钥
private String[] scopes; //客户受限范围
private String authorizedGrantTypes; // 授权客户端使用的授权类型
// private String authorities;//授予客户端的权限
private int tokenExpire;//token过期时间
private int tokenRefresh;//token 刷新时间
}

在application.yml 中填写具体参数:

authorizationparam:
client-id: 123456 #客户端id
secret: 123456 #(可信客户端需要)客户端密钥
scopes: read,write #客户受限范围
authorized-grant-types: password #授权客户端使用的授权类型
token-expire: 10000 #token过期时间
token-refresh: 1500 #token刷新时间
debug: true

3.编写OAuth2.0配置类,注意,这里我使用redis来存储令牌(token)和具体的用户信息,这边便于后期水平扩张:

package com.lmolong.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
/***
* OAuth 相关配置
*/
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
private static final String SECRETPREFIX = "{noop}"; //spring security5 之后需要
@Autowired
private AuthenticationManager authenticationManager; //认证管理者
@Autowired
private UserDetailsService userDetailsService; // 用户信息服务
@Autowired
private TokenStore tokenStore; //保存令牌数据栈
@Autowired
private AuthorizationParam authorizationParam;
@Override
public void configure(ClientDetailsServiceConfigurer clents) throws Exception {
clents.inMemory()
.withClient(authorizationParam.getClientId()) //客户端ID
.authorizedGrantTypes(authorizationParam.getAuthorizedGrantTypes(),"refresh_token")//设置验证方式
.scopes(authorizationParam.getScopes())
// .secret("{noop}secret")
.secret(SECRETPREFIX+authorizationParam.getSecret())
.accessTokenValiditySeconds(authorizationParam.getTokenExpire()) //token过期时间
.refreshTokenValiditySeconds(authorizationParam.getTokenRefresh());//refresh过期时间
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints){
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Bean
public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory){
// return new InMemoryTokenStore(); //使用内存存储令牌 tokeStore
return new RedisTokenStore(redisConnectionFactory); //使用redis存储令牌
}
}

4.编写web拦截、资源拦截 配置:

package com.lmolong.config;
import cn.hutool.crypto.SecureUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class WebSecurityConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence)
{
return charSequence.toString();
//return SecureUtil.md5(charSequence.toString());
}
@Override
public boolean matches(CharSequence charSequence, String s) {
//return SecureUtil.md5(charSequence.toString()).equals(s);
return charSequence.toString().equals(s);
}
});
// auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("user1").password(new BCryptPasswordEncoder().encode("123456")).roles("USER");
}
}
package com.lmolong.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().csrf().disable();
}
}

5.这里配置密码的加密方式,这里我暂时未加密:

package com.lmolong.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.authentication.builders.AuthenticationManagerBuilder;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//对任何请求做拦截,如果是完整认证的话,就允许访问
http.authorizeRequests().anyRequest().fullyAuthenticated();
//配置登陆连接,允许访问 --认证接口直接调用/oauth/token
http.formLogin().loginPage("/login").failureUrl("/login?code=").permitAll();
//配置登出连接,允许访问
http.logout().logoutUrl("/logout").permitAll();
http.authorizeRequests().antMatchers("/oauth/authorize").permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}

6.扩张UserDetailService

package com.lmolong.service;
import org.springframework.security.core.userdetails.UserDetailsService;
//后期在此新增UserService的业务接口
public interface UserService extends UserDetailsService {
}

7.实现 UserService,扩展用户认证功能:

package com.lmolong.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lmolong.authbean.AuthUserDetail;
import com.lmolong.mapper.TUserMapper;
import com.lmolong.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.lmolong.pojo.TUser;
@Primary
@Service
public class UserServiceImpl implements UserService {
@Autowired
private TUserMapper tUserMapper;
@Override
public AuthUserDetail loadUserByUsername(String username) throws UsernameNotFoundException {
TUser param =new TUser();
param.setUsername(username);
TUser tUser = tUserMapper.selectOne(new QueryWrapper<>(param));
if(tUser==null){
throw new UsernameNotFoundException("用户不存在");
}else{
return UserDetailConverter.convert(tUser);
}
}
private static class UserDetailConverter {
static AuthUserDetail convert(TUser user) {
return new AuthUserDetail(user);
}
}
}

8.编写认证数据库实体类,注意,这里我用了lombok:

package com.lmolong.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TUser implements Serializable {
private Long id;
private String username;
private String password;
}

9.对应的Auth2.0权限封装类:

package com.lmolong.authbean;
import lombok.Data;
import org.springframework.security.core.userdetails.User;
import com.lmolong.pojo.TUser;
import java.util.Collections;
@Data
public class AuthUserDetail extends User {
private TUser tUser;
public AuthUserDetail(TUser user) {
super(user.getUsername(), user.getPassword(), true, true, true, true, Collections.EMPTY_SET);
this.tUser= user;
}
}

10.编写对应mapper接口:

package com.lmolong.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lmolong.pojo.TUser;
public interface TUserMapper extends BaseMapper<TUser> {
}

11.注意,在启动类中,我们要加上MapperScan注解:

package com.lmolong;

import com.lmolong.config.AuthorizationParam;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication

@EnableConfigurationProperties({AuthorizationParam.class})

@MapperScan("com.lmolong.mapper")

public class SpringcloudOauth2Application {

public static void main(String[] args) {

SpringApplication.run(SpringcloudOauth2Application.class, args);

}

}

12.编写获取用户信息接口:

package com.lmolong.controller;
import com.lmolong.authbean.AuthUserDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import com.lmolong.pojo.TUser;
@RestController
public class UserController {
@Autowired
private TokenStore tokenStore;
@PostMapping("/auth")
public String auth(@RequestHeader("Authorization") String auth){
AuthUserDetail authUserDetail = (AuthUserDetail) tokenStore.readAuthentication(auth.split(" ")[1]).getPrincipal();
TUser tUser = authUserDetail.getTUser();
return tUser.getUsername()+":"+tUser.getPassword();
}
}

13.测试:

通过postman调用:http://localhost:8080/oauth/token 获取令牌

获取token成功:

使用 Redis Desktop manager 查看数据,发现数据已经存到redis中了:

访问接口,根据token获取用户信息:

获取成功。

至此,这个授权微服务整合完毕,当然,可能还有很多不完善的地方,期待大神指导,让我能更加完善它,在此感谢。

源码地址:https://gitee.com/lmolong/springcloud-oauth2

作者:L墨龙

原文:https://my.oschina.net/linwl/blog/2998036

Spring security + oauth2.0 + redis + mybatis plus 搭建微服务的更多相关文章

  1. springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)

    项目security_simple(认证授权项目) 1.新建springboot项目 这儿选择springboot版本我选择的是2.0.6 点击finish后完成项目的创建 2.引入maven依赖  ...

  2. Spring Security OAuth2.0认证授权二:搭建资源服务

    在上一篇文章[Spring Security OAuth2.0认证授权一:框架搭建和认证测试](https://www.cnblogs.com/kuangdaoyizhimei/p/14250374. ...

  3. Spring Security OAuth2.0 - AuthorizationServer和ResourceServer分离

    <Spring Security实现OAuth2.0授权服务 - 基础版>和<Spring Security实现OAuth2.0授权服务 - 进阶版>两篇文章中介绍如何搭建OA ...

  4. Spring Security OAuth2.0认证授权三:使用JWT令牌

    Spring Security OAuth2.0系列文章: Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二: ...

  5. Spring Security OAuth2.0认证授权四:分布式系统认证授权

    Spring Security OAuth2.0认证授权系列文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授 ...

  6. Spring Security OAuth2.0认证授权五:用户信息扩展到jwt

    历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OA ...

  7. Spring Security OAuth2.0认证授权六:前后端分离下的登录授权

    历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OA ...

  8. 【OAuth2.0】Spring Security OAuth2.0篇之初识

    不吐不快 因为项目需求开始接触OAuth2.0授权协议.断断续续接触了有两周左右的时间.不得不吐槽的,依然是自己的学习习惯问题,总是着急想了解一切,习惯性地钻牛角尖去理解小的细节,而不是从宏观上去掌握 ...

  9. 基于spring boot2.0+spring security +oauth2.0+ jwt微服务架构

    github地址:https://github.com/hankuikuide/microservice-spring-security-oauth2 项目介绍 该项目是一个演示项目,主要演示了,基于 ...

随机推荐

  1. FireMonkey下的WndProc实现

    unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Types, System.UI ...

  2. 编写按键驱动以及在framework层上报按键事件

    平台信息:内核:linux3.10 系统:android6.0平台:RK3288 前言:本文主要实现的功能是在android系统中添加一个按键,在驱动层使用定时器,每隔1秒钟向上层发送按键实现,fra ...

  3. lvs+keepalived+nginx高性能负载均衡集群

    项目发布时候,别人还能访问呢? 双机主从热备 LVS作用 LVS是一个开源的软件,可以实现传输层四层负载均衡.LVS是Linux Virtual Server的缩写,意思是Linux虚拟服务器.目前有 ...

  4. linux应用之gcc编译器的安装及使用

    gcc是linux系统下功能十分强大的编译器. 本人使用的是CentOS 6.6 64位系统,由于在安装系统的时候并没有勾选安装gcc编译器,因此需要自行安装gcc编译器. 使用yum安装gcc 对于 ...

  5. DDD领域驱动之干货(二)

       基于仓储的实现 1.前言:本着第一节写的有些糊涂,主要是自己喜欢实干,不太喜欢用文字表述,就这样吧.下面切入正题. 博客园里面有很多的大佬,我这里就不一一解释概览,有兴趣的朋友可以去看大佬们写的 ...

  6. 浏览器端JS导出EXCEL

    浏览器端JS导出EXCEL FileSaver.js 实现了在本身不支持 HTML5 W3C saveAs() FileSaver 接口的浏览器支持文件保存.FileSaver.js 在客户端保存文件 ...

  7. linux c++ 连接mysql 数据库

    Mysql是数据库中的主流,因此我一直以为在Linux下配置会很很容易,结果Google了大半天,大部分网页只说了如何安装Mysql之类的废话,对如何使用C/C++连接Mysql却只字不提,或者提的方 ...

  8. Opencv— — water wave

    // define head function #ifndef PS_ALGORITHM_H_INCLUDED #define PS_ALGORITHM_H_INCLUDED #include < ...

  9. 2018值得选用的五个Linux服务器发行版

    [IT168 编译]据最新统计,目前Linux发行版约有300种,几乎都可以作为服务器系统运行.在Linux早期,几乎所有发行版都是“万能”发行版,专门的Linux服务器发行版并不火热,到21世纪初, ...

  10. FFmpeg常用命令 (三)流媒体

    前言 如此强大的FFmpeg,能够实现视频采集.视频格式转化.视频截图.视频添加水印.视频切片.视频录制.视频推流.更改音视频参数功能等.通过终端命令如何实现这些功能,Richy在本文做一记录,以备之 ...