SpringCloud+OAuth2实现统一权限验证,并持久化到Mysql中

社区群聊

添加小编微信进入java学习交流群,小编微信:372787553,备注进群

本文是基于数据库实现的,如果您想基于Redis实现,请移步到SpringCloud+OAuth2 统一权限验证

OAuth2简介

OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 2.0即完全废止了OAuth1.0。 OAuth
2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限

关键名词

在详细讲解OAuth 2.0之前,需要了解几个专用名词。它们对读懂后面的讲解,尤其是几张图,至关重要。
(1) Third-party application:第三方应用程序,
(2)HTTP service:HTTP服务提供商,本文中简称"服务提供商",
(3)Resource Owner:资源所有者
(4)User Agent:用户代理,本文中就是指浏览器。
(5)Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器
(6)Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。
知道了上面这些名词,就不难理解,OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动。

以上介绍节选自上一篇文章SpringCloud+OAuth2 统一权限验证https://blog.csdn.net/weixin_38937840/article/details/90321037
这篇文章使用的是Redis存储token,本文我们将介绍使用JDBC的方式存储token

搭建项目

创建服务

javayh-server是统一认证服务器;
javayh-resource是资源服务器;
在这里插入图片描述

核心依赖

由于篇幅较长,这里我只列出了一部分,文章末尾会有源码地址

  <!-- Spring Security -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.tomcat</groupId>
                    <artifactId>tomcat-jdbc</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <!-- druid 官方 starter -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.connector.version}</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>${pagehelpe.version}</version>
        </dependency>

数据库脚本

由于SQL脚本所占的篇幅过长,我放在了另一个文档里,点击查看
初始化SQL https://blog.csdn.net/weixin_38937840/article/details/105382901

认证服务器配置

以上的准备工作都完成了,我们就可以配置认证服务器了;

配置统一认证处理

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration
		extends AuthorizationServerConfigurerAdapter {

	@Autowired(required = false)
	private AuthenticationManager authenticationManager;

	@Autowired
	private DataSource dataSource;

	@Autowired
	@Qualifier("userDetailsServiceImpl")
	private UserDetailsService userDetailsService;

	/**
	 * 自定义获取客户端,为了支持自定义权限,
	 */
	@Bean
	public ClientDetailsService clientDetails() {
		JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(
				dataSource);
		jdbcClientDetailsService.setPasswordEncoder(new BCryptPasswordEncoder());
		return jdbcClientDetailsService;
	}

	/**
	 * 令牌存放
	 * @return
	 */
	@Bean
	public TokenStore tokenStore() {
		// 基于 JDBC 实现,令牌保存到数据
		return new JdbcTokenStore(dataSource);
	}

	/**
	 * 授权store
	 * @return
	 */
	@Bean
	public ApprovalStore approvalStore() {
		return new JdbcApprovalStore(dataSource);
	}

	/**
	 * 令牌信息拓展
	 * @return
	 */
	@Bean
	public TokenEnhancer tokenEnhancer() {
		return new BaseTokenEnhancer();
	}

	/**
	 * 授权码
	 * @return
	 */
	@Bean
	public AuthorizationCodeServices authorizationCodeServices() {
		return new JdbcAuthorizationCodeServices(dataSource);
	}

	/**
	 * 配置客户端详细信息服务
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.jdbc(dataSource);
		clients.withClientDetails(clientDetails());
	}

	/**
	 * 令牌访问端点
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints)
			throws Exception {
		endpoints
				// 允许post提交
				.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
				// 密码模式需要
				.authenticationManager(authenticationManager)
				.approvalStore(approvalStore()).userDetailsService(userDetailsService)
				.tokenServices(createDefaultTokenServices())
				.accessTokenConverter(SecurityHelper.buildAccessTokenConverter())
				// 设置令牌存储在数据库
				.tokenStore(tokenStore())
				.authorizationCodeServices(authorizationCodeServices());
	}

	private DefaultTokenServices createDefaultTokenServices() {
		DefaultTokenServices tokenServices = new DefaultTokenServices();
		// 令牌存储策略
		tokenServices.setTokenStore(tokenStore());
		tokenServices.setTokenEnhancer(tokenEnhancer());
		// 是否产生刷新令牌
		tokenServices.setSupportRefreshToken(true);
		tokenServices.setReuseRefreshToken(true);
		tokenServices.setClientDetailsService(clientDetails());
		return tokenServices;
	}

	/**
	 * 令牌访问端点安全策略
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security)
			throws Exception {
		security
				// 开启/oauth/check_token验证端口认证权限访问
				.checkTokenAccess("isAuthenticated()")
				// 开启表单认证,申请令牌
				.allowFormAuthenticationForClients()
				// 允许表单验证
				.checkTokenAccess("permitAll()");
	}
}

服务器安全配置

package com.javayh.oauth2.server.conf;

import com.javayh.oauth2.server.conf.service.UserDetailsServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * <p>
 *       授权回调
 * </p>
 * @version 1.0.0
 * @author Dylan-haiji
 * @since 2020/4/8
 */
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

	@Bean
	public BCryptPasswordEncoder passwordEncoder() {
		// 设置默认的加密方式
		return new BCryptPasswordEncoder();
	}

	@Bean
	@Override
	public UserDetailsService userDetailsService() {
		return new UserDetailsServiceImpl();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// 使用自定义认证与授权
		auth.userDetailsService(userDetailsService());
	}

	@Override
	public void configure(WebSecurity web) throws Exception {
		// 将 check_token 暴露出去,否则资源服务器访问时报 403 错误
		web.ignoring().antMatchers("/oauth/check_token");
	}

}

自定义权限配置

package com.javayh.oauth2.server.conf.service;

import com.google.common.collect.Lists;
import com.javayh.oauth.domain.TbPermission;
import com.javayh.oauth.domain.TbUser;
import com.javayh.oauth2.server.service.TbPermissionService;
import com.javayh.oauth2.server.service.TbUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 自定义用户认证与授权
 * </p>
 *
 * @version 1.0.0
 * @author Dylan-haiji
 * @since 2020/4/5
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

	@Autowired
	private TbUserService tbUserService;

	@Autowired
	private TbPermissionService tbPermissionService;

	@Override
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException {
		TbUser tbUser = tbUserService.getByUsername(username);
		List<GrantedAuthority> grantedAuthorities = Lists.newArrayList();
		if (tbUser != null) {
			// 获取用户授权
			List<TbPermission> tbPermissions = tbPermissionService
					.selectByUserId(tbUser.getId());

			// 声明用户授权
			tbPermissions.forEach(tbPermission -> {
				if (tbPermission != null && tbPermission.getEnname() != null) {
					GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(
							tbPermission.getEnname());
					grantedAuthorities.add(grantedAuthority);
				}
			});
		}
		return new User(tbUser.getUsername(), tbUser.getPassword(), grantedAuthorities);
	}

}

到这授权服务器配置完成,关于配置文件,请参考github配置文件 https://github.com/Dylan-haiji/javayh-platform/tree/master/javayh-sso/javayh-server/src/main/resources

资源服务器配置

资源服务器配置

package com.javayh.resource.conf;

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Configuration;
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.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

/**
 * <p>
 *       资源服务器配置
 * </p>
 * @version 1.0.0
 * @author Dylan-haiji
 * @since 2020/4/8
 */
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

	@Override
	public void configure(HttpSecurity http) throws Exception {
		ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
				.antMatcher("/**").authorizeRequests();
		http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
				.and().authorizeRequests()
				// 指定监控访问权限
				.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll().anyRequest()
				.authenticated().and().exceptionHandling()
				// 认证鉴权错误处理,为了统一异常处理。每个资源服务器都应该加上。
				// .accessDeniedHandler(new AccessDeniedHandler())
				// .authenticationEntryPoint(new WebException())
				// 配置跨域
				.and().cors().and().csrf().disable()
				// 禁用httpBasic
				.httpBasic().disable();
		registry.anyRequest().authenticated();
	}

}

配置文件

这里只写了一部分,详情参考https://github.com/Dylan-haiji/javayh-platform/blob/master/javayh-sso/javayh-resource/src/main/resources/bootstrap.yml

security:
  oauth2:
    client:
      client-id: client
      client-secret: secret
      access-token-uri: http://localhost:9090/oauth/token
      user-authorization-uri: http://localhost:9090/oauth/authorize
    resource:
      token-info-uri: http://localhost:9090/oauth/check_token

调用流程

获取token

  • 启动服务服务获取code

访问:http://localhost:9090/oauth/authorize?client_id=client&response_type=code

  • 初次需要登录,如下图

登录名:admin , 密码:123456

在这里插入图片描述

  • 获取code
    在这里插入图片描述

  • 获取token
    这里我们使用postman获取token

访问:http://client:secret@localhost:9090/oauth/token

如下图:
在这里插入图片描述
详细步骤:https://github.com/Dylan-haiji/javayh-platform/tree/master/javayh-sso/javayh-server

根据token获取资源

  • 访问资源

http://localhost:9099/api/user/info

这时有两种方式访问

本问全部源码以放在github上,欢迎点赞,或者加入开发,也可加入社区交流群,添加小编微信:372787553
地址:https://github.com/Dylan-haiji/javayh-platform/tree/master/javayh-sso

本文的分享暂时就到这里,希望对您有所帮助
关注 Java有货领取更多资料

联系小编。微信:372787553,带您进群互相学习
左侧小编微信,右侧获取免费资料
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_38937840/article/details/105382447