公司要从单一架构转为微服务架构,特此记录
前期准备
初始依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.5.RELEASE</version> <relativePath/> </parent>
使用spring boot 2.3.5.RELEASE,这是公司统一的版本
数据库脚本
Oauth数据库表,直接用官方的SQL表结构: sql表结构
或者用下面的
CREATE SCHEMA IF NOT EXISTS `oauth2` DEFAULT CHARACTER SET utf8 ; USE `oauth2` ; -- ----------------------------------------------------- -- Table `oauth2`.`clientdetails` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`clientdetails` ( `appId` VARCHAR(128) NOT NULL, `resourceIds` VARCHAR(256) NULL DEFAULT NULL, `appSecret` VARCHAR(256) NULL DEFAULT NULL, `scope` VARCHAR(256) NULL DEFAULT NULL, `grantTypes` VARCHAR(256) NULL DEFAULT NULL, `redirectUrl` VARCHAR(256) NULL DEFAULT NULL, `authorities` VARCHAR(256) NULL DEFAULT NULL, `access_token_validity` INT(11) NULL DEFAULT NULL, `refresh_token_validity` INT(11) NULL DEFAULT NULL, `additionalInformation` VARCHAR(4096) NULL DEFAULT NULL, `autoApproveScopes` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`appId`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_access_token` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_access_token` ( `token_id` VARCHAR(256) NULL DEFAULT NULL, `token` BLOB NULL DEFAULT NULL, `authentication_id` VARCHAR(128) NOT NULL, `user_name` VARCHAR(256) NULL DEFAULT NULL, `client_id` VARCHAR(256) NULL DEFAULT NULL, `authentication` BLOB NULL DEFAULT NULL, `refresh_token` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`authentication_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_approvals` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_approvals` ( `userId` VARCHAR(256) NULL DEFAULT NULL, `clientId` VARCHAR(256) NULL DEFAULT NULL, `scope` VARCHAR(256) NULL DEFAULT NULL, `status` VARCHAR(10) NULL DEFAULT NULL, `expiresAt` DATETIME NULL DEFAULT NULL, `lastModifiedAt` DATETIME NULL DEFAULT NULL) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_client_details` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_client_details` ( `client_id` VARCHAR(128) NOT NULL, `resource_ids` VARCHAR(256) NULL DEFAULT NULL, `client_secret` VARCHAR(256) NULL DEFAULT NULL, `scope` VARCHAR(256) NULL DEFAULT NULL, `authorized_grant_types` VARCHAR(256) NULL DEFAULT NULL, `web_server_redirect_uri` VARCHAR(256) NULL DEFAULT NULL, `authorities` VARCHAR(256) NULL DEFAULT NULL, `access_token_validity` INT(11) NULL DEFAULT NULL, `refresh_token_validity` INT(11) NULL DEFAULT NULL, `additional_information` VARCHAR(4096) NULL DEFAULT NULL, `autoapprove` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`client_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_client_token` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_client_token` ( `token_id` VARCHAR(256) NULL DEFAULT NULL, `token` BLOB NULL DEFAULT NULL, `authentication_id` VARCHAR(128) NOT NULL, `user_name` VARCHAR(256) NULL DEFAULT NULL, `client_id` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`authentication_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_code` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_code` ( `code` VARCHAR(256) NULL DEFAULT NULL, `authentication` BLOB NULL DEFAULT NULL) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_refresh_token` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_refresh_token` ( `token_id` VARCHAR(256) NULL DEFAULT NULL, `token` BLOB NULL DEFAULT NULL, `authentication` BLOB NULL DEFAULT NULL) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8;
整合spring security
引入security的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
配置拦截
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { /** * 密码编码器 * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override @Bean public UserDetailsService userDetailsService() { return new UserDetailsServiceImpl(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()) .passwordEncoder(passwordEncoder()); } /** * 安全拦截机制 * * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/**").authenticated() .and() .formLogin().permitAll() .and() .csrf().disable(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
实现UserDetailsService ,加载用户信息
public class UserDetailsServiceImpl implements UserDetailsService { private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); @Resource private BugLoginuserMapper userMapper; @SneakyThrows @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { BugLoginuser userInfo = userMapper.selectByPrimaryKey(username); if (userInfo == null) { log.error("Not found user:{}", username); throw new AuthException("用户不存在或密码错误"); } List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); SimpleGrantedAuthority authority = new SimpleGrantedAuthority("Role_"+userInfo.getAuthtype()); grantedAuthorities.add(authority); return new User(userInfo.getUserMainId(), userInfo.getUserPassword(), grantedAuthorities); } }
整合oauth2.0
引入依赖
<dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.2.6.RELEASE</version> </dependency>
编写认证服务器的拦截器,直接使用数据库进行存储认证信息
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Resource private DataSource dataSource; @Resource private AuthenticationManager authenticationManager; @Resource private JwtTokenStore jwtTokenStore; @Resource private JwtAccessTokenConverter jwtAccessTokenConverter; @Bean public TokenStore tokenStore() { // //基于内存存储令牌 // return new InMemoryTokenStore(); //基于jdbc存储token return new JdbcTokenStore(dataSource); } /** * 令牌端点,设置令牌 * * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore()) .authenticationManager(authenticationManager) .tokenStore(jwtTokenStore) .accessTokenConverter(jwtAccessTokenConverter); } /** * 基于jdbc存储客户端信息,需要先进行配置 * * @return */ public ClientDetailsService clientDetailsService() { return new JdbcClientDetailsService(dataSource); } /** * 配置客户端信息 * * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(clientDetailsService()); } /** * 配置令牌端点的安全约束 * @param security * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security .tokenKeyAccess("permitAll()")//tokenKey这个endpoint完全公开 .checkTokenAccess("permitAll()")//checkToken这个endpoint完全公开 .allowFormAuthenticationForClients();//允许表单验证 } }
访问如下链接
http://localhost:8088/oauth/authorize?client_id=client&response_type=code
被拦截到security的登录页面
输入用户名密码,会跳转到授权页面,点击授权就会回调到客户端
跳转到百度,拿到code码
https://www.baidu.com/?code=t2ocYs
拿着授权码去请求token
相关路径
/oauth/authorize:授权端点
/oauth/token:令牌端点
/oauth/confirm_access:用户确认授权提交端点
/oauth/error:授权服务错误信息端点
/oauth/check_token:用于资源服务访问的令牌解析端点
获取token
访问资源服务器