序
本文主要简单介绍一下spring security oauth2的client_credentials模式
maven
| <dependency> |
| <groupId>org.springframework.security.oauth</groupId> |
| <artifactId>spring-security-oauth2</artifactId> |
| </dependency> |
| <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> |
auth server config
| @Configuration |
| @EnableAuthorizationServer |
| public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter { |
| |
| |
| @Override |
| public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { |
| oauthServer |
| .tokenKeyAccess("permitAll()") |
| .checkTokenAccess("isAuthenticated()") |
| .allowFormAuthenticationForClients(); |
| } |
| |
| @Override |
| public void configure(ClientDetailsServiceConfigurer clients) throws Exception { |
| clients.inMemory() |
| .withClient("demoApp") |
| .secret("demoAppSecret") |
| .authorizedGrantTypes("client_credentials", "password", "refresh_token") |
| .scopes("all") |
| .resourceIds("oauth2-resource") |
| .accessTokenValiditySeconds(1200) |
| .refreshTokenValiditySeconds(50000); |
| } |
| |
| } |
resource server config
| @Configuration |
| @EnableResourceServer |
| public class ResourceServerConfig extends ResourceServerConfigurerAdapter { |
| |
| } |
| |
demo controller
| @RestController |
| @RequestMapping("/api") |
| public class DemoController { |
| |
| @GetMapping("/blog/{id}") |
| public String getBlogById(@PathVariable long id) { |
| return "this is blog "+id; |
| } |
| } |
验证
没有token请求资源
| curl -i -H "Accept: application/json" -X GET http://localhost:8080/api/blog/1 |
返回
| HTTP/1.1 401 |
| X-Content-Type-Options: nosniff |
| X-XSS-Protection: 1; mode=block |
| Cache-Control: no-cache, no-store, max-age=0, must-revalidate |
| Pragma: no-cache |
| Expires: 0 |
| X-Frame-Options: DENY |
| Cache-Control: no-store |
| Pragma: no-cache |
| WWW-Authenticate: Bearer realm="oauth2-resource", error="unauthorized", error_description="Full authentication is required to access this resource" |
| Content-Type: application/json;charset=UTF-8 |
| Transfer-Encoding: chunked |
| Date: Sat, 02 Dec 2017 14:31:51 GMT |
| |
| {"error":"unauthorized","error_description":"Full authentication is required to access this resource"} |
client_credentials请求授权
| curl -H "Accept: application/json" demoApp:demoAppSecret@localhost:8080/oauth/token -d grant_type=client_credentials |
或者
| curl -H "Accept: application/json" http://localhost:8080/oauth/token -d "grant_type=client_credentials&client_id=demoApp&client_secret=demoAppSecret" |
返回
| {"access_token":"6d0ee2b2-c803-49bf-a813-a25bfb59a976","token_type":"bearer","expires_in":1199,"scope":"all"} |
携带token请求资源
| curl -i -H "Accept: application/json" -H "Authorization: Bearer 6d0ee2b2-c803-49bf-a813-a25bfb59a976" -X GET http://localhost:8080/api/blog/1 |
或者
| curl -i -X GET http://localhost:8080/api/blog/1?access_token=fe8bcab3-1d33-4ef1-b1d0-bd142a480af2 |
不过这种把token暴露在url中不是太安全
返回
| HTTP/1.1 200 |
| X-Content-Type-Options: nosniff |
| X-XSS-Protection: 1; mode=block |
| Cache-Control: no-cache, no-store, max-age=0, must-revalidate |
| Pragma: no-cache |
| Expires: 0 |
| X-Frame-Options: DENY |
| X-Application-Context: application |
| Content-Type: application/json;charset=UTF-8 |
| Content-Length: 14 |
| Date: Sat, 02 Dec 2017 14:31:09 GMT |
| |
| this is blog 1 |
check token
| curl -i -X POST -H "Accept: application/json" -u "demoApp:demoAppSecret" http: |
返回
| HTTP/1.1 403 |
| X-Content-Type-Options: nosniff |
| X-XSS-Protection: 1; mode=block |
| Cache-Control: no-cache, no-store, max-age=0, must-revalidate |
| Pragma: no-cache |
| Expires: 0 |
| X-Frame-Options: DENY |
| Content-Type: application/json;charset=UTF-8 |
| Transfer-Encoding: chunked |
| Date: Sat, 02 Dec 2017 14:50:32 GMT |
| |
| {"timestamp":1512226232386,"status":403,"error":"Forbidden","message":"Access is denied","path":"/oauth/check_token"} |
需要配置
| @Override |
| public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { |
| oauthServer |
| .tokenKeyAccess("permitAll()") |
| .checkTokenAccess("isAuthenticated()") |
| .allowFormAuthenticationForClients(); |
| } |
成功返回
| HTTP/1.1 200 |
| X-Content-Type-Options: nosniff |
| X-XSS-Protection: 1; mode=block |
| Cache-Control: no-cache, no-store, max-age=0, must-revalidate |
| Pragma: no-cache |
| Expires: 0 |
| X-Frame-Options: DENY |
| X-Application-Context: application |
| Content-Type: application/json;charset=UTF-8 |
| Transfer-Encoding: chunked |
| Date: Sat, 02 Dec 2017 14:48:33 GMT |
| |
| {"aud":["oauth2-resource"],"scope":["read"],"exp":1512227200,"client_id":"demoApp"} |
token非法
| HTTP/1.1 400 |
| X-Content-Type-Options: nosniff |
| X-XSS-Protection: 1; mode=block |
| Cache-Control: no-cache, no-store, max-age=0, must-revalidate |
| Pragma: no-cache |
| Expires: 0 |
| X-Frame-Options: DENY |
| X-Application-Context: application |
| Cache-Control: no-store |
| Pragma: no-cache |
| Content-Type: application/json;charset=UTF-8 |
| Transfer-Encoding: chunked |
| Date: Sat, 02 Dec 2017 14:51:33 GMT |
| Connection: close |
| |
| {"error":"invalid_token","error_description":"Token was not recognised"} |
doc
增加了文件,另外mvn依赖需要写版本号
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>

package com.italkbb.homesecurity.alertmessage.security;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
//import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
/**
* Created by wangyunfei on 2017/6/9.
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @Autowired
// private DomainUserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
// .antMatchers("/api-docs/**").permitAll();
}
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
/*
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
*/
//不定义没有password grant_type
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/* 替换这个不工作,报 null 当调用userDetailsService loadUser时候。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
*/
/* @Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("");
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
*/
@Bean
public PasswordEncoder passwordEncoder(){
// return new BCryptPasswordEncoder();
return new PasswordEncoder() {
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
System.out.println("PasswordEncoder: raw password:" + rawPassword.toString() + " encoded:" + encodedPassword
+ "================================");
return true;
}
@Override
public String encode(CharSequence rawPassword) {
System.out.println("PasswordEncoder: raw password:" + rawPassword.toString() + "================================");
return rawPassword.toString();
}
};
}
}

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2018-01-16 spring代码异常捕获到logback logging.config=logback-spring.xml文件中不能输出异常e.printStackTrace
2015-01-16 android关闭日志
2015-01-16 android intent和intent action大全
2015-01-16 调用新浪微博显示用户信息