Spring security + oauth2.0 + redis + mybatis plus 搭建微服务
上个星期一个朋友请求帮忙,让我搭建一个分布式授权中心的微服务,之前我也没搭建过,在网上撸了几天前辈们写的技术博客,搞出个模型,分享给大家:
前辈们博客地址:
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 搭建微服务的更多相关文章
- springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)
项目security_simple(认证授权项目) 1.新建springboot项目 这儿选择springboot版本我选择的是2.0.6 点击finish后完成项目的创建 2.引入maven依赖 ...
- Spring Security OAuth2.0认证授权二:搭建资源服务
在上一篇文章[Spring Security OAuth2.0认证授权一:框架搭建和认证测试](https://www.cnblogs.com/kuangdaoyizhimei/p/14250374. ...
- Spring Security OAuth2.0 - AuthorizationServer和ResourceServer分离
<Spring Security实现OAuth2.0授权服务 - 基础版>和<Spring Security实现OAuth2.0授权服务 - 进阶版>两篇文章中介绍如何搭建OA ...
- Spring Security OAuth2.0认证授权三:使用JWT令牌
Spring Security OAuth2.0系列文章: Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二: ...
- Spring Security OAuth2.0认证授权四:分布式系统认证授权
Spring Security OAuth2.0认证授权系列文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授 ...
- Spring Security OAuth2.0认证授权五:用户信息扩展到jwt
历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OA ...
- Spring Security OAuth2.0认证授权六:前后端分离下的登录授权
历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OA ...
- 【OAuth2.0】Spring Security OAuth2.0篇之初识
不吐不快 因为项目需求开始接触OAuth2.0授权协议.断断续续接触了有两周左右的时间.不得不吐槽的,依然是自己的学习习惯问题,总是着急想了解一切,习惯性地钻牛角尖去理解小的细节,而不是从宏观上去掌握 ...
- 基于spring boot2.0+spring security +oauth2.0+ jwt微服务架构
github地址:https://github.com/hankuikuide/microservice-spring-security-oauth2 项目介绍 该项目是一个演示项目,主要演示了,基于 ...
随机推荐
- RequestDispatcher用法
1.具体用法: RequestDispatcher dispatcher = req.getRequestDispatcher("updateByUserId_001.jsp"); ...
- Apache http server和tomcat的区别
Apache官方网站:http://www.apache.org/Tomcat官方网站:http://tomcat.apache.org/ 1. Apache是web服务器,Tomcat是应用(jav ...
- PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib64/php/modules/redis.so' - /usr/lib64/php/modules/redis.so: undefined symbol: igbinary_serialize in Unknown on line 0
网上找到两种解决方法, 1.先删除php-pecl-redis,再用pecl install redis 2.安装php-igbinary模块,不过我安装时包冲突了,没有安装成功,所以用了第一种方法
- BZOJ 2002 Bounce 弹飞绵羊 —— 分块算法
题目链接:https://vjudge.net/problem/HYSBZ-2002 2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec Memory Li ...
- jmeter使用笔记——流程及常用组件配置
添加线程组 线程数 :对应用户数, Ramp-Up: 多少秒启动这些线程,1秒代表1秒内启动设置的线程数,10秒代表10秒内启动线程数 循环次数: 每个线程执行线程组内的请求循环次数 调度器:可以对线 ...
- 打造基于Ubuntu+XBMC的家庭媒体中心
作为一名高清爱好者,一直想配置一台HTPC放家里实现高清播放外加家庭服务器功能.Nvidia的Ion平台自然是高清平台的硬件首选,而家庭媒体中心的软件端则首先考虑开发的已经很成熟的开源利器 XBMC ...
- codeforces B. Calendar 解题报告
题目链接:http://codeforces.com/problemset/problem/304/B 题目意思:给出两个日期,需要算出这两个日期之间有多少日. 细心模拟就可以了.特别要注意的是,两个 ...
- python学习笔记:第三天(数字)
Python3 数字(Number) 1. 数字数据类型 用于存储数值.数据类型是不允许改变的,这就意味着如果改变数字数据类型得值,将重新分配内存空间. 实例在变量赋值时 Number 对象将被创建, ...
- linux应用之perl环境的安装(centos)
1.安装Perl环境 yum install perl*这个命令基本上把perl的模块给安装齐了.yum install cpanCPAN这个就不用说了吧,大家都懂. 如果你对perl模块版本要求比较 ...
- [AH2017/HNOI2017]抛硬币
传送门 这个题的暴力比较好想--然后用一些组合的知识就可以变成正解了. 首先我们考虑a=b的情况.我们把扔出来的硬币看成是一个01序列,那么对于一个b获胜的序列,他在每一位都按位异或1之后必然是一个a ...