认证、资源服务器共用一个依赖
<artifactId>spring-security-oauth2-jjwt-demoartifactId>
<packaging>pompackaging>
<modules>
<module>authorization-servermodule>
<module>resource-servermodule>
modules>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-oauth2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-jwtartifactId>
<version>1.0.9.RELEASEversion>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR8version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
认证服务器的配置主要包括四点:启用授权服务、客户端详情服务、配置令牌的访问端点和令牌服务、配置令牌端点的安全约束
启用授权服务:用于标识当前服务是一个授权服务 — 在配置类中增加@EnableAuthorizationServer即可
客户端详情服务: 用于配置需要认证的客户端相关信息
// 重写AuthorizationServerConfigurerAdapter类中的configure(ClientDetailsServiceConfigurer clients)方法(注意参数),并在此方法中配置客户端信息。
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() // 客户端信息存储位置
// 客户端详情
.withClient("client") // 客户端ID
.secret(passwordEncoder.encode("878412")) // 客户端密码
.resourceIds("user") // 客户端允许访问的资源
.redirectUris("http://localhost:8082/test")// 回调地址
.scopes("all")// 授权范围
// .authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token") // 授权类型:授权码模式、密码模式、客户端模式、简化模式
.authorizedGrantTypes("authorization_code") // 授权类型。
.autoApprove(false);
// 可以在and()后面配置另外一个客户端的信息.
}
}
配置令牌的访问端点和令牌服务
// 在TokenConfig中创建令牌的存储策略(内存存储)
@Configuration
public class TokenConfig {
@Bean
public TokenStore tokenStore(){
// 令牌存储策略:内存方式
return new InMemoryTokenStore();
}
}
//
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private TokenStore tokenStore;
// 令牌管理服务
public AuthorizationServerTokenServices tokenServices(){
DefaultTokenServices services = new DefaultTokenServices();
services.setClientDetailsService(clientDetailsService); // 客户端详情服务器(第2点中已经配好的),自动注入
services.setSupportRefreshToken(true); // 支持刷新令牌
services.setTokenStore(tokenStore); // 令牌存储方式(在TokenConfig中配置的)
services.setAccessTokenValiditySeconds(7200); // 令牌过期时间
services.setRefreshTokenValiditySeconds(259200); // 刷新令牌过期时间
return services;
}
//授权码的存储方式
@Bean
public AuthorizationCodeServices authorizationCodeServices(){
return new InMemoryAuthorizationCodeServices();
}
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager; // 认证管理器自动注入
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 令牌访问端点,包括:/authrize、/token、/confirm_access、/error、/check_token、/token_key(前缀为/oauth)
endpoints
.authenticationManager(authenticationManager) // 认证管理器(参见后面的说明)
.authorizationCodeServices(authorizationCodeServices) // 授权码服务(参见上面authorizationCodeServices方法的定义)
.tokenServices(tokenServices()) // 令牌管理服务(见上面的方法定义)
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
}
认证管理器定义
// 配置SecurityConfig ,并在此配置文件中重写WebSecurityConfigurerAdapter类中的authenticationManagerBean方法创建出AuthenticationManager对象,同时还需要配置UserDetailsService
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
// 实现 UserDetailsService
@Service
public class UserService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String pwd = passwordEncoder.encode("123456");
return new User(username, pwd,
AuthorityUtils.commaSeparatedStringToAuthorityList("admin,sysadmin"));
}
}
配置令牌端点的安全约束
令牌的端点服务允许如何方法
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
}
获取授权码
http://localhost:8081/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://localhost:8082/test&scope=all
参数说明:
response_type: code --授权码
client_id: client – AuthorizationServerConfig配置文件中配置的withClient
redirect_uri:http://localhost:8082/test – AuthorizationServerConfig配置文件中配置的redirectUris。
scope:all – 授权类型 ,AuthorizationServerConfig配置文件中配置的scopes。
请求上面的地址后先会跳转到登录页面,使用UserService配置的账号密码登录后会跳转到授权页面,授权后才会跳转到百度,并附带授权码(本例中没有校验账号,只校验密码为123456即可,所以账号可以随意输入,密码为123456)
用获取到的授权码再去请求授权令牌(使用postman来测试)
地址:http://localhost:8081/oauth/token
参数:
参数名 | 值 |
---|---|
grant_type | authorization_code |
client_id | client |
client_secret | 客户端设置的密码 |
redirect_uri | http://localhost:8082/test |
scope | all |
code | 上面获取到的授权吗 |
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.resourceId("user")
.tokenServices(tokenServices())
.stateless(true);
}
// token校验方法
public ResourceServerTokenServices tokenServices(){
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:8081/oauth/check_token");
remoteTokenServices.setClientId("client");
remoteTokenServices.setClientSecret("878412");
return remoteTokenServices;
}
}
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/test/**")
.permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.csrf()
.disable()
;
}
}
资源
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("getCurrentUser")
public Object getCurrentUser(Authentication authentication){
return authentication.getPrincipal();
}
}
3.依据授权令牌请求资源
地址:http://localhost:8082/user/getCurrentUser
授权方式:bearer token