springboot 2.1.6 集成Spring Security oauth2 —— 2.3.6.RELEASE ---集成jwt,redis, client信息存储数据库(二)

 

 

认证服务器:

package com.oath.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
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.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;

@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
    private PasswordEncoder passwordEncoder;

	@Autowired
	private UserDetailsService userDetailsService;

	@Autowired
	private TokenStore tokenStore;

	@Autowired
	private AccessTokenConverter accessTokenConverter;

	@Autowired
	private ClientDetailsService clientDetails;

	// 配置令牌端点(Token Endpoint)的安全约束
	@Override
	public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
		oauthServer
				// code授权添加
				.realm("oauth2-resources")
				// 开启/oauth/token_key验证端口无权限访问
				.tokenKeyAccess("permitAll()")
				// 接口/oauth/check_token允许检查令牌
				.checkTokenAccess("isAuthenticated()")
				// 使/oauth/token支持client_id以及client_secret作登录认证
				.allowFormAuthenticationForClients()
				// 密码编码器
				.passwordEncoder(passwordEncoder);
	}

	// 配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

		endpoints
				// 认证管理器
				.authenticationManager(authenticationManager)
				// 允许 GET、POST 请求获取 token,即访问端点:oauth/token
				.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
				// 要使用refresh_token的话,需要额外配置userDetailsService
				.userDetailsService(userDetailsService)
				// 指定token存储位置
				.tokenStore(tokenStore)
				// 配置JwtAccessToken转换器
				.accessTokenConverter(accessTokenConverter)
				// 客户端详细信息服务的基本实现 这里使用JdbcClientDetailsService
				.setClientDetailsService(clientDetails);
	}

	// 配置客户端详情服务
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// 内存模式
		/**
		 * clients.inMemory().withClient("demoApp").secret(bCryptPasswordEncoder.encode("demoAppSecret"))
				.redirectUris("http://baidu.com")// code授权添加
				.authorizedGrantTypes("authorization_code", "client_credentials", "password", "refresh_token")
				// scopes的值就是all(全部权限),read,write等权限。就是第三方访问资源的一个权限,访问范围
				.scopes("all")
				// 这个资源服务的ID,这个属性是可选的,但是推荐设置并在授权服务中进行验证。
				.resourceIds("oauth2-resource")
				// 设置accessTokenValiditySeconds属性来设置Access Token的存活时间。
				.accessTokenValiditySeconds(1200)
				// 设置refreshTokenValiditySeconds属性来设置refresh Token的存活时间。
				.refreshTokenValiditySeconds(50000);
		 */

		// 数据库模式
		clients.withClientDetails(clientDetails); // 表中存储的secret值是加密后的值,并非明文;
	}
}

资源服务器:

package com.oath.config;

import org.springframework.context.annotation.Configuration;
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 ResourceServerConfig extends ResourceServerConfigurerAdapter {
	@Override
	public void configure(HttpSecurity http) throws Exception {
		http
                .authorizeRequests()
				.antMatchers("/oauth/confirm_access").permitAll()
				.antMatchers("/**/*.js").permitAll()
				.antMatchers("/favicon.ico").permitAll()
                .and()
                .requestMatchers().antMatchers("/api/**").and().authorizeRequests().antMatchers("/api/**").authenticated();
	}
}
package com.oath.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;

@Configuration
public class AuthenticationBeanConfig {
	
	@Autowired
	private DataSource dataSource;
	
	@Bean
    @ConditionalOnMissingBean(PasswordEncoder.class)
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

	@Bean
	@ConditionalOnMissingBean(ClientDetailsService.class)
	public ClientDetailsService clientDetails() {
		return new JdbcClientDetailsService(dataSource);
	}
}
package com.oath.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component("userDetailsService")
public class DefaultUserDetailsService implements UserDetailsService {

	private static final Logger log = LoggerFactory.getLogger(DefaultUserDetailsService.class);

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		log.debug("当前登陆用户名为:{}", username);
		String password = passwordEncoder.encode("123456");
		log.debug("当前登陆用户名密码为:{}", password);

		/**
		 * isEnabled 账户是否启用 
		 * isAccountNonExpired 账户没有过期
		 * isCredentialsNonExpired 身份认证是否是有效的 
		 * isAccountNonLocked 账户没有被锁定 
		 * 对于 isAccountNonLocked 和 isEnabled 没有做业务处理,只是抛出了对于的异常信息;
		 */
		
		// 赋予一个admin权限

		// User admin = new User(username, password, true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
		return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER"));
	}

}

安全配置:

package com.oath.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
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.redis.RedisTokenStore;

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
    private PasswordEncoder passwordEncoder;
	
	@Autowired
	private UserDetailsService userDetailsService;

	@Autowired
	private RedisConnectionFactory connectionFactory;

	@Override
	public void configure(HttpSecurity http) throws Exception {
		http
			.requestMatchers()
			.antMatchers("/oauth/**", "/login/**", "/logout/**")
			.and()
			.authorizeRequests()
			.and()
			.formLogin()
			.permitAll()
			.and()
			.cors().disable();
	}

	@Bean
	public TokenStore tokenStore() {
		// 使用redis存储token信息
		RedisTokenStore redisTokenStore = new RedisTokenStore(connectionFactory);
		return redisTokenStore;

		// 使用jwt内存存储token信息
//		JwtTokenStore jwtTokenStore = new JwtTokenStore(accessTokenConverter());
//		return jwtTokenStore;
	}

	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		converter.setSigningKey("healthy");
		return converter;
	}

	@Bean
	@Primary
	public DefaultTokenServices tokenServices() {
		DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
		defaultTokenServices.setTokenStore(tokenStore());
		return defaultTokenServices;
	}

	/**
	 * *需要配置这个支持password模式 support password grant type
	 */
	@Override
	@Bean
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
	}

	public static void main(String[] args) {
		String encode = new BCryptPasswordEncoder().encode("demoAppSecret");
		System.out.println(encode);
	}
}

测试接口:

package com.oath.controller;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class HelloOath2Controller {
	@RequestMapping("/hello/{id}")
	public String helloOath2(@PathVariable long id) {
		System.out.println("请求的ID编码为:" + id);
		return "helloOath2";
	}
}

 

启动类:

package com.oath;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SecurityOauth2DemoApplication {

	/**
	 * *【密码授权模式-client】
	 * 		密码模式需要参数:username,password,grant_type,client_id,client_secret
	 * 		http://localhost:8080/oauth/token?username=demoUser1&password=123456&grant_type=password&client_id=demoApp&client_secret=demoAppSecret
	 * 
	 * *【客户端授权模式-password】 客户端模式需要参数:grant_type,client_id,client_secret
	 * 		http://localhost:8080/oauth/token?grant_type=client_credentials&client_id=demoApp&client_secret=demoAppSecret
	 * 
	 * *【授权码模式-code】 获取code
	 * 		http://localhost:8080/oauth/authorize?response_type=code&client_id=demoApp&redirect_uri=http://baidu.com
	 * 
	 * *【通过code】 换token
	 * 		http://localhost:8080/oauth/token?grant_type=authorization_code&code=Filepd&client_id=demoApp&client_secret=demoAppSecret&redirect_uri=http://baidu.com
	 * 		这里的code字段是授权码模式中返回的code  例如: https://www.baidu.com/?code=tsuHSh
	 *
	 * *【通过code】 换token-标准实现请求中不效验client_secret参数
	 *		注:配置com.oath.config.OAuth2ServerConfig&configure(AuthorizationServerSecurityConfigurer oauthServer)中去掉:allowFormAuthenticationForClients()配置即可
	 * 		http://localhost:8080/oauth/token?grant_type=authorization_code&code=Filepd&client_id=demoApp&redirect_uri=http://baidu.com
	 * 		这里的code字段是授权码模式中返回的code  例如: https://www.baidu.com/?code=tsuHSh
	 * 
	 * *【通过refresh token】 刷新token
	 *		http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=7ba47059-d853-4050-9c64-69d0cade71a7&client_id=demoApp&client_secret=demoAppSecret
	 *		其中grant_type为固定值:grant_type=refresh_token, refresh_token = 通过code获取的token中的refresh_token
	 *
	 * *【效验token】 是否合法
	 * 		http://localhost:8080/oauth/check_token?token=7b6efb3e-1b72-4089-81a9-1580a0eb7a70
	 * 		
	 * *【访问 API】 参数需要access_token
	 *		http://localhost:8080/api/hello/1?access_token=7b6efb3e-1b72-4089-81a9-1580a0eb7a70
	 *
	 */ 

	public static void main(String[] args) {
		SpringApplication.run(SecurityOauth2DemoApplication.class, args);
	}

}

POM文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.oath.demo</groupId>
	<artifactId>security-oauth2-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>security-oauth2-demo</name>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.security.oauth</groupId>
			<artifactId>spring-security-oauth2</artifactId>
			<version>2.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-jwt</artifactId>
			<version>1.0.10.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>


		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>public</id>
			<name>aliyun nexus</name>
			<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
		</repository>
	</repositories>

	<pluginRepositories>
		<pluginRepository>
			<id>public</id>
			<name>aliyun nexus</name>
			<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>

</project>

数据库文件:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(48) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `access_token_validity` int(11) NULL DEFAULT NULL,
  `refresh_token_validity` int(11) NULL DEFAULT NULL,
  `additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('demoApp', 'oauth2-resource', '$2a$10$z6DXhwPrzQe4wk9nGmqLvO6zQzYEAYscmNaAaDTDVhhuJGrhqZzk.', 'all', 'authorization_code,client_credentials,password,refresh_token', 'http://baidu.com', 'ROLE_CLIENT', 3600, 3600, NULL, NULL);

SET FOREIGN_KEY_CHECKS = 1;

properties:

spring.datasource.url=jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver


# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000

logback:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

	<property name="log.path" value="/home/logs" />
	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />

	<!-- 控制台输出 -->
	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>${log.pattern}</pattern>
		</encoder>
	</appender>
	
	<!-- 显示形成的sql、使用的参数、结果集 -->
	<!--
		<logger name="java.sql" level="debug" />
		<logger name="org.springframework.jdbc" level="debug" />
	-->

	<logger name="com.oath" level="debug" />
	<logger name="org.springframework" level="debug" />

	<root level="info">
		<appender-ref ref="console" />
	</root>
</configuration> 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值