一、客戶(hù)端模式
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、配置文件,基礎(chǔ)端口,工程名,redis配置
spring.application.name=serve-security
server.port=8080
# Redis數(shù)據(jù)庫(kù)索引(默認(rèn)為0)
spring.redis.database=0
# Redis服務(wù)器地址
spring.redis.host=127.0.0.1
# Redis服務(wù)器連接端口
spring.redis.port=6379
# Redis服務(wù)器連接密碼(默認(rèn)為空)
spring.redis.password=
# 連接池最大連接數(shù)(使用負(fù)值表示沒(méi)有限制)
spring.redis.pool.max-active=8
# 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閑連接
spring.redis.pool.max-idle=8
# 連接池中的最小空閑連接
spring.redis.pool.min-idle=0
# 連接超時(shí)時(shí)間(毫秒)
spring.redis.timeout=0
3、AuthorizationServerConfig 認(rèn)證服務(wù)器配置(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.授權(quán)碼模式(authorization code)
* 2.簡(jiǎn)化模式(implicit)
* 3.密碼模式(resource owner password credentials)
* 4.客戶(hù)端模式(client credentials)
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("123456");
//配置一個(gè)用于client認(rèn)證
clients.inMemory()
//client模式
.withClient("client_1")
.resourceIds(RESOURCE_IDS)
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret(finalSecret);
}
/**
* 認(rèn)證服務(wù)端點(diǎn)配置
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
//用戶(hù)管理
.userDetailsService(userDetailsService)
//token存到redis
.tokenStore(new RedisTokenStore(redisConnectionFactory))
//啟用oauth2管理
.authenticationManager(authenticationManager)
//接收GET和POST
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
/**
* 用來(lái)配置令牌端點(diǎn)(Token Endpoint)的安全約束
* @param oauthServer
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.allowFormAuthenticationForClients();
}
}
4、RedisConfig工具類(lèi)
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, 存儲(chǔ)二進(jìn)制字節(jié)碼, 所以自定義序列化類(lèi),方便調(diào)試redis
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//使用Jackson2JsonRedisSerializer來(lái)序列化和反序列化redis的value值
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
//使用StringRedisSerializer來(lái)序列化和反序列化redis的key
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//開(kāi)啟事務(wù)
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,否則會(huì)報(bào)錯(cuò)
* @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;
}
/**
* 通過(guò)HttpSecurity實(shí)現(xiàn)Security的自定義過(guò)濾配置
*
* @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:對(duì)應(yīng)的AuthorizationServerConfig.configure(ClientDetailsServiceConfigurer clients)中配置的scopes中的參數(shù)
client_id:模塊名稱(chēng),withClient對(duì)應(yīng)的值
client_secret:密碼,secret加密之前的參數(shù)
返回:
{
"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"
}
三、授權(quán)碼模式
1、代碼在上面的AuthorizationServerConfig.java代碼中方法為configure(ClientDetailsServiceConfigurer clients)
里面:
clients.inMemory()
//授權(quán)碼模式
.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、啟動(dòng)測(cè)試:

response_type固定為:code
會(huì)跳轉(zhuǎn)到登錄頁(yè)面,輸入上面設(shè)置的用戶(hù):user 密碼:123456 就會(huì)到授權(quán)碼頁(yè)面。

選擇approve,就會(huì)跳轉(zhuǎn)到對(duì)應(yīng)的頁(yè)面,同時(shí)url后添加code=44MlYe。
4、常見(jiàn)問(wèn)題:
a)User must be authenticated with Spring Security before authorization can be completed.
網(wǎng)上很多人認(rèn)為這個(gè)報(bào)錯(cuò)是加載問(wèn)題,大部分是設(shè)置:security.oauth2.resource.filter-order = 3或者認(rèn)為是版本問(wèn)題。
其實(shí)除了這個(gè)設(shè)置方式意外就是注意WebSecurityConfig.configure(HttpSecurity httpSecurity)這個(gè)方法放開(kāi)/oauth/**。
b) 其他異常情況,大部分是參數(shù)傳參問(wèn)題,注意下代碼和上面請(qǐng)求的參數(shù),另外注意跳轉(zhuǎn)的uri的路徑,避免跨域問(wèn)題
四、簡(jiǎn)化模式
整體流程通授權(quán)碼模式
AuthorizationServerConfig.configure(ClientDetailsServiceConfigurer clients) 中代碼authorizedGrantTypes設(shè)置為 .authorizedGrantTypes("implicit")
access_token:表示訪(fǎng)問(wèn)令牌,必選項(xiàng)。
token_type:表示令牌類(lèi)型,該值大小寫(xiě)不敏感,必選項(xiàng)。
expires_in:表示過(guò)期時(shí)間,單位為秒。如果省略該參數(shù),必須其他方式設(shè)置過(guò)期時(shí)間。
scope:表示權(quán)限范圍,如果與客戶(hù)端申請(qǐng)的范圍一致,此項(xiàng)可省略。
state:如果客戶(hù)端的請(qǐng)求中包含這個(gè)參數(shù),認(rèn)證服務(wù)器的回應(yīng)也必須一模一樣包含這個(gè)參數(shù)。
response_type:固定token