这是我参与「掘金日新计划 · 6 月更文挑战」的第30天 ,点击查看活动详情
Spring Security OAuth架构简介
1、服务器端点
-
授权端点:生成授权码code\
-
Token端点:颁发access_token\
-
校验端点:校验access_token\
-
吊销端点:主动吊销非法access_token\
2、Spring Security OAuth架构
(1)用户访问客户应用程序,经过过滤器验证,如果没有权限则执行步骤(2),如果有权限则执行正常的业务逻辑(service层)
(2)如果没有权限则访问授权服务器的授权端点,授权端点调用Spring Security OAuth提供的工具类生成授权码
(3)用户获得授权码后使用OAuth2RestTemplate请求授权服务器颁发access_token,授权服务器Token端点调用Spring Security OAuth提供的工具类生成access_token
(4)用户获得access_token后使用OAuth2RestTemplate向资源服务器请求资源,此时资源服务器上的ResourceServerTokenService会执行对access_token的校验,同时access_tokenOAuth2AuthenticationManager会从中获取用户的相关信息
密码模式(Resource Owner Password Credentials)与授权码模式的区别是申请令牌不再使用授权码,而是直接通过用户名和密码即可申请令牌。
一、修改授权服务器配置
1、修改OAuth2AuthorizationServer
使其可以同时接受授权码和密码模式
// 添加商户信息
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
......
// 同时支持授权码模式和密码模式
.authorizedGrantTypes("authorization_code", "password")
......
}
\
2、发送请求获取令牌
Post请求:http://localhost:8081/oauth/token
参数列表如下:\
- grant_type:密码模式授权填写password
- username:账号
- password:密码 并且此链接需要使用 http Basic认证。
\
二、如何从数据库获取用户信息
1、创建业务层
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
ClientDetailsService clientDetailsService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//***********以下为授权码模式
//取出商户身份,如果身份为空说明没有认证
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证client_id和client_secret
if(authentication==null){
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username);
if(clientDetails!=null){
//密码
String clientSecret = clientDetails.getClientSecret();
return new User(username,clientSecret,AuthorityUtils.commaSeparatedStringToAuthorityList(""));
}
}
//***********以下为用户名密码模式
if (StringUtils.isEmpty(username)) {
return null;
}
System.out.println(username);
//取出正确密码(hash值)
String password = "123456";
//使用用户名和密码去数据库校验,校验成功则放行否则返回null校验失败
if(!"helen".equals(username) || !"123456".equals(password)){
return null;
}
//用户权限,这里暂时使用静态数据,最终会从数据库读取
UserDetails userDetails = new User(username,
new BCryptPasswordEncoder().encode("123456"),
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_A, ROLE_B, ROLE_C"));
return userDetails;
}
}
2、修改授权服务器配置
OAuth2AuthorizationServer
屏蔽之前的UserDetailsService的创建,如下
@Bean
UserDetailsService userDetailsService(){
......
}
修改之前的注入,注入自定义的业务层实现
//注入用户信息
//@Autowired
@Resource(name = "userDetailsServiceImpl")
UserDetailsService userDetailsService;