什么是OAuth2
OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本。
授权服务器代码
pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Security OAuth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.4.0.RELEASE</version>
</dependency>
</dependencies>
启动类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
UserDetails登录
package com.service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
GrantedAuthority grantedAuthority = null;
if(username.equals("name1")){
grantedAuthority = new SimpleGrantedAuthority("name1");
}else if(username.equals("name2")){
grantedAuthority = new SimpleGrantedAuthority("name2");
}else if(username.equals("name3")){
grantedAuthority = new SimpleGrantedAuthority("name3");
}else if(username.equals("name4")){
grantedAuthority = new SimpleGrantedAuthority("name4");
}else if(username.equals("name5")){
grantedAuthority = new SimpleGrantedAuthority("name5");
}else if(username.equals("name6")){
grantedAuthority = new SimpleGrantedAuthority("name6");
}
grantedAuthorities.add(grantedAuthority);
return new User(username, new BCryptPasswordEncoder().encode("123456"), grantedAuthorities);
}
}
config
WebSecurityConfiguration
package com.config;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Resource
UserDetailsServiceImpl userDetailsService;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
// 设置默认的加密方式
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Override
public void configure(WebSecurity web) throws Exception {
// 将 check_token 暴露出去,否则资源服务器访问时报 403 错误
web.ignoring().antMatchers("/oauth/check_token");
}
}
AuthorizationServerConfiguration
package com.config;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
// 注入 WebSecurityConfiguration 中配置的 BCryptPasswordEncoder
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client")
// 还需要为 secret 加密
.secret(passwordEncoder.encode("secret"))
.authorizedGrantTypes("authorization_code")
.scopes("app")
//.autoApprove(true)//自动授权
.redirectUris("http://localhost:8082/getcode");
}
}
获取code
get请求
http://localhost:8080/oauth/authorize?client_id=client&response_type=code
获取token
post请求
http://client:secret@localhost:8080/oauth/token?grant_type=authorization_code&code=XYZJNL
也可以在登录之前先启动token获取服务器直接获取
资源服务器代码
pom
同上
启动类
同上
ResourceServerConfiguration
package com.config;
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Primary
@Bean
public RemoteTokenServices remoteTokenServices() {
final RemoteTokenServices tokenServices = new RemoteTokenServices();
//设置授权服务器check_token端点完整地址
tokenServices.
setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
//设置客户端id与secret,注意:client_secret值不能使用passwordEncoder加密!
tokenServices.setClientId("client");
tokenServices.setClientSecret("secret");
return tokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.and()
.sessionManagement().sessionCreationPolicy
(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 以下为配置所需保护的资源路径及权限,需要与认证服务器配置的授权部分对应
.antMatchers("/name1/**").hasAuthority("name1")
.antMatchers("/name2/**").hasAuthority("name2")
.antMatchers("/name3/**").hasAuthority("name3")
.antMatchers("/name4/**").hasAuthority("name4")
.antMatchers("/name5/**").hasAuthority("name5")
.antMatchers("/name6/**").hasAuthority("name6");
}
}
controller
package com.controller;
@RestController
public class UserController {
@RequestMapping("/name1")
public String name1(){
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
return "用户名:"+principal.toString();
}
@RequestMapping("/name2")
public String name2(){
return "name2";
}
@RequestMapping("/name3")
public String name3(){
return "name3";
}
@RequestMapping("/name4")
public String name4(){
return "name4";
}
@RequestMapping("/name5")
public String name5(){
return "name5";
}
@RequestMapping("/name6")
public String name6(){
return "name6";
}
}
application.yml
server:
port: 8081
logging:
level:
root: INFO
org.springframework.web: INFO
org.springframework.security: INFO
org.springframework.security.oauth2: INFO
访问资源
get或post都可以
http://localhost:8081/name1?access_token=60de7fb7-6511-4a18-808b-04c3ab6b53c6
也可以在header上加如:
'Authorization': 'e9ca9dcf-12bc-4e25-8831-afd69d4760ec'
获取token服务器代码
pom app同上
package com.controller;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.support.BasicAuthorizationInterceptor;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class TokenController {
@RequestMapping("/getcode")
public String getCode(String code){
RestTemplate restTemplate = new RestTemplate();
// 请求地址client:secret@
String url = "http://localhost:8080/oauth/token";
restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor("client", "secret"));
// 请求头设置,x-www-form-urlencoded格式的数据
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//提交参数设置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("grant_type","authorization_code");
map.add("code",code);
// 组装请求体
HttpEntity<MultiValueMap<String, String>> request =
new HttpEntity<MultiValueMap<String, String>>(map, headers);
// 发送post请求,并打印结果,以String类型接收响应结果JSON字符串
String tokenjson = restTemplate.postForObject(url, request, String.class);
return tokenjson;
}
}