当前位置:网站首页>Oauth2.0 authentication server construction
Oauth2.0 authentication server construction
2022-08-02 16:05:00 【zhangyu,】
核心 POM
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
配置 WebSecurityConfig 开启 Spring 方法级的安全保护
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
/** * 开启Spring方法级的安全保护 */
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/** * In the authorization code mode, a request is made in the browser address bar to obtain it code * .anyRequest().authenticated() Authentication interception must be performed on this request,When it is found that the user is not logged in, a login box will pop up, This allows the user to enter a username and password to log in, If the request is released, The login page cannot pop up, 并抛出 InsufficientAuthenticationException * .httpBasic() Because the user is not logged in to access a protected resource, So turn it on httpBasic 进行简单认证, 否则会抛出 AccessDeniedException 异常, */
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//关闭跨域保护
.authorizeRequests()
.antMatchers("/captcha/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//永远不会创建HttpSession, 默认配置
.and()
.headers().cacheControl().disable()//禁用缓存
;
}
/** * Inject an authentication manager, Does not implement authentication by itself, Instead, it authenticates to authentication providers one by one, Until an authentication provider can successfully verify the identity of the current user * * AuthenticationManager(认证管理器接口) 的默认实现类 ProviderManager, 管理多个 AuthenticationProvider(认证提供者) */
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
配置 TokenConfig 来定义 Token 的生成方式
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/** * 配置token */
@Configuration
public class TokenConfig {
@Bean
public TokenStore jwtTokenStore() {
//The token storage scheme is adoptedJWT
return new JwtTokenStore(jwtAccessTokenConverter());
}
/* * AccessToken转换器: 定义 token 的生成方式 * JwtAccessTokenConverter: 表示采用 JWT 来生成 */
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(OauthConstant.OAUTH_SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
return converter;
}
}
配置 UserDetailsService 和 JdbcClientDetailsService Get user and client information
Custom client table structure
-- oauth2.0 By default there is a set of client table structures that can be replaced,以下为自定义,The table structure of other users can be defined by themselves
CREATE TABLE `sys_client` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`client_id` varchar(32) NOT NULL COMMENT '客户端ID',
`resource_ids` varchar(256) DEFAULT NULL COMMENT '客户端密钥',
`client_secret` varchar(256) DEFAULT NULL COMMENT '资源ID列表',
`scope` varchar(256) DEFAULT NULL COMMENT '作用域',
`authorized_grant_types` varchar(256) DEFAULT NULL COMMENT '授权方式',
`web_server_redirect_uri` varchar(256) DEFAULT NULL COMMENT '回调地址',
`authorities` varchar(256) DEFAULT NULL COMMENT '权限列表',
`access_token_validity` int DEFAULT NULL COMMENT '令牌有效时间',
`refresh_token_validity` int DEFAULT NULL COMMENT 'Refresh token validity time',
`additional_information` varchar(4096) DEFAULT NULL COMMENT '扩展信息',
`autoapprove` varchar(256) DEFAULT NULL COMMENT 'Whether to release automatically',
`deleted` char(1) DEFAULT '0' COMMENT '删除标记,1:已删除,0:正常',
`platform_id` int NOT NULL DEFAULT '0' COMMENT '所属平台',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Client Information Sheet';
-- Insert two test clients
INSERT INTO `platform`.`sys_client`(`id`, `client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`, `deleted`, `platform_id`) VALUES (1, 'c1', '', '$2a$10$1Bg3qCxNVXobR2SJG9t0zOV45glOCH1MpvvPJDdyXCycWu/rZ1DOa', 'all', 'refresh_token,authorization_code,client_credentials,implicit', 'http://www.baidu.com', NULL, 72000, 259200, NULL, 'false', '0', 0);
INSERT INTO `platform`.`sys_client`(`id`, `client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`, `deleted`, `platform_id`) VALUES (2, 'c2', 'mo-wen-res', '$2a$10$1Bg3qCxNVXobR2SJG9t0zOV45glOCH1MpvvPJDdyXCycWu/rZ1DOa', 'all', 'password,refresh_token,captcha,authorization_code', 'http://www.baidu.com', NULL, 72000, 259200, NULL, 'false', '0', 0);
-- 查询客户端信息
String OAUTH_SQL_CLIENT = "SELECT client_id, client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove\n" +
"FROM sys_client WHERE client_id = ? AND deleted = 0";
-- 查询用户信息
String OAUTH_SQL_LOGIN_USER = "select user_id as id, `name`, phone, password from sys_user where phone = ?";
-- 查询用户权限
String OAUTH_SQL_USER_PERMISSION = "SELECT `name` FROM sys_permission WHERE id IN(\n" +
" SELECT permission_id FROM sys_role_permission WHERE role_id IN(\n" +
" SELECT role_id FROM sys_user_role WHERE user_id = ?\n" +
" )\n" +
")";
定义 UserDetailsService The implementation class is used to query user information
@Slf4j
@Service
@AllArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
// 查询用户信息的service, 自行定义
private final UserService userService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
LoginUser loginUser = userService.getUserByPhone(s);
List<String> authorities = userService.getPermissionsByUserId(loginUser.getId());
log.info("当前登陆用户: [{}] 权限: [{}]", loginUser, authorities);
return User.withUsername(toJSONString(loginUser))
.password(loginUser.getPassword())
.authorities(authorities.toArray(new String[0]))
.build();
}
}
重写 JdbcClientDetailsService Used to query client information
@Slf4j
@Service
public class JdbcClientDetailsServiceImpl extends JdbcClientDetailsService {
public JdbcClientDetailsServiceImpl(DataSource dataSource) {
super(dataSource);
}
@Resource
private PasswordEncoder bCryptPasswordEncoder;
@Override
public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
super.setSelectClientDetailsSql(OauthConstant.OAUTH_SQL_CLIENT);
super.setPasswordEncoder(bCryptPasswordEncoder);
ClientDetails clientDetails = super.loadClientByClientId(clientId);
log.info("加载客户端信息: [{}], [{}]", clientId, clientDetails);
return clientDetails;
}
//The encryption method used for the password
@Bean
public PasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
继承 AuthorizationServerConfigurerAdapter To implement the core configuration of the authentication server️️️️️️️️️️️️
@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final JdbcClientDetailsServiceImpl clientDetailsServiceImpl;
private final UserDetailsServiceImpl userDetailsServiceImpl;
private final TokenStore jwtTokenStore;
private final JwtAccessTokenConverter jwtAccessTokenConverter;
private final AuthenticationManager authenticationManager;
private final PasswordEncoder bCryptPasswordEncoder;
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
private final CustomWebResponseExceptionTranslator customWebResponseExceptionTranslator;
/** * 用来配置令牌端点的安全约束, 密码校验方式等 */
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.allowFormAuthenticationForClients()
.passwordEncoder(bCryptPasswordEncoder)
.tokenKeyAccess("permitAll()") //oauth/token_key是公开
.checkTokenAccess("permitAll()") //oauth/check_token公开
;
}
/** * 用来配置客户端详情服务(ClientDetailsService), 客户端详情信息在这里进行初始化 */
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsServiceImpl);
}
/** * 用来配置令牌(token)的访问端点和令牌服务 * Configure the access endpoint for the token:That is, to apply for a tokenURL .pathMapping(defaultPath, customPath) * 令牌服务:Token generation and issuance rules(TokenConfig) */
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.userDetailsService(userDetailsServiceImpl)
.authenticationManager(authenticationManager)//认证管理器,PasswordMode configuration required
.authorizationCodeServices(authorizationCodeServices())//授权码模式codeStorage method definition
.tokenStore(jwtTokenStore)//采用jwt方式管理token
.accessTokenConverter(jwtAccessTokenConverter)//jwt增强,定义自己的SigningKey
.allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET)
.reuseRefreshTokens(false)//Indicates that the refresh token is reused.That is to say, the first request will be reused all the time refresh_token, So ban it
;
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
//设置授权码模式的授权码如何存取,Temporarily use memory mode
return new InMemoryAuthorizationCodeServices();
}
}
测试端口
- 获取 code:Get /oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
- Simplified schema acquisition token:Get /oauth/authorize?response_type=token&client_id=c1&redirect_uri=http://www.baidu.com
- 密码模式获取 token:Post /oauth/token
- grant_type:password
- username:用户名
- password:密码
- Authorization:Basic YzI6NDU2 — The request header adds client information
- 授权码模式获取 token:Post /oauth/token
- grant_type:authorization_code
- code:获取到的 code
- redirect_uri:重定向url
- Authorization:Basic YzI6NDU2 — The request header adds client information
- 刷新 token:Post /oauth/token
- grant_type:refresh_token
- refresh_token:get gettoken时得到的refresh_token
- Authorization:Basic YzI6NDU2 — The request header adds client information
Oauth2.0 系列文章
以下是同步到语雀的、可读性好一点,CSDN 继续看的点专栏就好.
Oauth2.0 核心篇
Oauth2.0 安全性(以微信授权登陆为例)
Oauth2.0 认证服务器搭建
Oauth2.0 添加验证码登陆方式
Oauth2.0 资源服务器搭建
Oauth2.0 自定义响应值以及异常处理
Oauth2.0 补充
边栏推荐
猜你喜欢
随机推荐
win10无法识别蓝牙麦克风
基类和派生类的关系【继承】/多态和虚函数/【继承和多态】抽象类和简单工厂
CDH (computational Diffie-Hellman) problem and its differences with discrete logarithm and DDH problems
How does ns3 solve cross reference issue
golang内存相关文章-收集
【Solidity智能合约基础】-- 基础运算与底层位运算
HCIE学习记录——OSI参考模型
三方对接接口数据安全问题
change the available bandwidth of tcp flow dynamically in mininet
关于推荐系统的随想
光学好书推荐
排序方法汇总(C语言)
lua编程
mininet multihomed topology
消息队列的技术选型
unity-shader(中级)
2021-03-12
TypeScript
在mininet中测试arp欺骗
剑指offer:数值的整数次方