一、客户端模式
1、pom.xml核心jar包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置文件,基础端口,工程名,redis配置
spring.application.name=serve-security
server.port=8080
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
3、AuthorizationServerConfig 认证服务器配置(spring security oauth2的http配置)
package com.sun.securityserve.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.token.store.redis.RedisTokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String RESOURCE_IDS = "order";
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
private UserDetailsService userDetailsService;
/**authorizedGrantTypes:
* 1.授权码模式(authorization code)
* 2.简化模式(implicit)
* 3.密码模式(resource owner password credentials)
* 4.客户端模式(client credentials)
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("123456");
//配置一个用于client认证
clients.inMemory()
//client模式
.withClient("client_1")
.resourceIds(RESOURCE_IDS)
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret(finalSecret);
}
/**
* 认证服务端点配置
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
//用户管理
.userDetailsService(userDetailsService)
//token存到redis
.tokenStore(new RedisTokenStore(redisConnectionFactory))
//启用oauth2管理
.authenticationManager(authenticationManager)
//接收GET和POST
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
/**
* 用来配置令牌端点(Token Endpoint)的安全约束
* @param oauthServer
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.allowFormAuthenticationForClients();
}
}
4、RedisConfig工具类
package com.sun.securityserve.config;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类,方便调试redis
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
//使用StringRedisSerializer来序列化和反序列化redis的key
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//开启事务
redisTemplate.setEnableTransactionSupport(true);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
/**
* 自定义生成key的策略
*
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return target.getClass().getSimpleName() + "_"
+ method.getName() + "_"
+ StringUtils.arrayToDelimitedString(params, "_");
}
};
}
}
5、WebSecurityConfig 拦截配置(spring security的http配置)
package com.sun.securityserve.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.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.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定义承接AuthorizationServerConfig 中的userDetailsService,否则会报错
* @return
*/
@Bean
@Override
protected UserDetailsService userDetailsService() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String finalPassword = "{bcrypt}" + bCryptPasswordEncoder.encode("123456");
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password(finalPassword).authorities("USER").build());
manager.createUser(User.withUsername("admin").password(finalPassword).authorities("USER").build());
return manager;
}
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
/**
* 注入AuthenticationManager接口,启用OAuth2密码模式
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
/**
* 通过HttpSecurity实现Security的自定义过滤配置
*
* @param httpSecurity
* @throws Exception
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.and()
.authorizeRequests()
.antMatchers("/security/**").authenticated();
}
}
grant_type:固定为client_credentials
scope:对应的AuthorizationServerConfig.configure(ClientDetailsServiceConfigurer clients)中配置的scopes中的参数
client_id:模块名称,withClient对应的值
client_secret:密码,secret加密之前的参数
返回:
{
"access_token": "71d384ed-24c1-4945-be58-aa059e47bbda",
"token_type": "bearer",
"expires_in": 40356,
"scope": "select"
}
二、密码模式
代码在上面的AuthorizationServerConfig.java代码中方法为configure(ClientDetailsServiceConfigurer clients)
里面
clients.inMemory()
//client模式
.withClient("client_1")
.resourceIds(RESOURCE_IDS)
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret(finalSecret)
.and()
//添加密码模式
.withClient("client_2")
.resourceIds(RESOURCE_IDS)
.authorizedGrantTypes("password", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret(finalSecret);
{
"access_token": "304de3f4-4ba3-475c-84dc-6efd8369becf",
"token_type": "bearer",
"refresh_token": "a093e2f1-a4ae-436c-a8b6-bab79869e283",
"expires_in": 43199,
"scope": "select"
}
三、授权码模式
1、代码在上面的AuthorizationServerConfig.java代码中方法为configure(ClientDetailsServiceConfigurer clients)
里面:
clients.inMemory()
//授权码模式
.withClient("client_code")
.secret(finalSecret)
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("all")
.redirectUris("http://localhost:8080/security/login")
.accessTokenValiditySeconds(1200)
.refreshTokenValiditySeconds(50000);
2、修改WebSecurityConfig.java中的configure(HttpSecurity httpSecurity) 方法
改为:
httpSecurity.httpBasic().and().authorizeRequests()
.antMatchers("/js/**", "/css/**", "/images/**","/oauth/**")
.permitAll()
.anyRequest()
.permitAll()
.and().csrf().disable();
3、启动测试:
image.pngresponse_type固定为:code
会跳转到登录页面,输入上面设置的用户:user 密码:123456 就会到授权码页面。
image.png
选择approve,就会跳转到对应的页面,同时url后添加code=44MlYe。
4、常见问题:
a)User must be authenticated with Spring Security before authorization can be completed.
网上很多人认为这个报错是加载问题,大部分是设置:security.oauth2.resource.filter-order = 3或者认为是版本问题。
其实除了这个设置方式意外就是注意WebSecurityConfig.configure(HttpSecurity httpSecurity)这个方法放开/oauth/**。
b) 其他异常情况,大部分是参数传参问题,注意下代码和上面请求的参数,另外注意跳转的uri的路径,避免跨域问题
四、简化模式
整体流程通授权码模式
AuthorizationServerConfig.configure(ClientDetailsServiceConfigurer clients) 中代码authorizedGrantTypes设置为 .authorizedGrantTypes("implicit")
access_token:表示访问令牌,必选项。
token_type:表示令牌类型,该值大小写不敏感,必选项。
expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。
response_type:固定token
网友评论