Spring Security oAuth2的理解
名词解释
oAuth
定义:是一个协议。
目的:为用户资源的授权提供了一个安全的,开放,简单的标准。| 为用户授权提供安全标准。
优点:与其他授权方式不同,该标准授权不会让第三方接触到用户的账号信息。 | 该标准根本就不存用户信息。
Spring Security
定义:这是个spring提供的安全框架。
Spring Security oAuth2
定义:Spring基于oAuth2协议的安全框架。
对象:授权服务(客户端)和受保护的资源服务。
对象特点:这两者不是必须分开的,两者好多情况是属于一个模块中,可以选择按照对象为维度将服务进行拆分,注意这历史选择而不是必须。
Spring Security oAuth2的个人理解
预设网址
spring asecurity oauth2这套框架会预留两个网址:
1.AuthorizationEndpoint(授权端点)
/oauth/authorize 用来给用户授权
image验证成功后询问是否为用户授权
image2.TokenEndpoint
用来给服务提供token
预设网址:/oauth/token
image实现
对于所有spring security OAuth2提供的功能适配器,可以用@Configuration配置类实现。对于授权和认证的服务配置我这里都以配置类来实现。
AuthorizationServerConfigurerAdapter/授权服务配置
这个实际类似于对应 /oauth/authorize token的配置
@Configuration //配置类
@EnableAuthorizationServer //用于开启授权服务,
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
}
授权功能点
1.ClientDetailsServiceConfigurer
定义客户端详细信息服务的配置程序。可以初始化客户端详细信息。
属性
clientId:(必填)客户端ID。
secret:(对于受信任的客户端是必需的)客户端密钥(如果有)。
scope:客户端的范围受到限制。如果范围未定义或为空(默认值),则客户端不受范围的限制。
authorizedGrantTypes:授权客户使用的授权类型。默认值为空。
authorities:授予客户端的权限(常规的Spring Security权限)。
@Autowired
private BCryptPasswordEncoder passwordEncoder; 注意这里是autowired 这个bean在web安全配置里配的。
/** * 配置客户端,定义客户端 * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients // 使用内存设置 先定义客户端的模式,可以选择内存模式,数据库模式 .inMemory() // client_id .withClient("client") // client_secret .secret(passwordEncoder.encode("secret")) // 授权类型,密码模式和刷新令牌 .authorizedGrantTypes("password", "refresh_token") // 授权范围 backend 后端 .scopes("backend") // 可以设置对哪些资源有访问权限,不设置则全部资源都可以访问 .resourceIds("backend-resources") // 设置访问令牌的有效期,这里是 1 天 .accessTokenValiditySeconds(60 * 60 * 24) // 设置刷新令牌的有效期,这里是 30 天 .refreshTokenValiditySeconds(60 * 60 * 24 * 30); } //定义jdbc模式 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 客户端配置 clients.withClientDetails(jdbcClientDetailsService()); }
2.AuthorizationServerSecurityConfigurer
定义令牌的安全约束
一般定义允许客户端访问token,并允通过表单获取token
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 允许客户端访问 /oauth/check_token 检查 token
.checkTokenAccess("isAuthenticated()")
//允许客户端表单验证
.allowFormAuthenticationForClients();
}
checkTokenAccess和allowFormAuthenticationForClients两个配完后客户端就可以不用通过预设的授权页面授权了,直接通过表单提交验证。
3.AuthorizationServerEndpointsConfigurer
定义授权,令牌端点,令牌服务。
一般用来定义授权模式,token存储模式。
// 定义token的存储模式
@Bean
public TokenStore tokenStore() {
//内存存储膜模式
return new InMemoryTokenStore();
}
@Bean
public TokenStore tokenStore() {
// 基于 JDBC 实现,令牌保存到数据库
return new JdbcTokenStore(dataSource());
}
//密码授权注入
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 用于支持密码模式
endpoints.authenticationManager(authenticationManager)
//token存储模式
.tokenStore(tokenStore());
}
逻辑
先定义授权访问方式 AuthorizationServerSecurityConfigurer 在定义 授权支持的模式和token存储方式 AuthorizationServerEndpointsConfigurer 最后 定义授权客户端/访问者的信息,如定义访问者,如果用内存模式
那么访问者的信息就都会存储在内存,如果定义在数据库,那么访问者的信息就都会在数据库,如定义客户端token的有效期等。
以上对应token的维护即授权, 先定义允许访问授权预设路径, 在定义token的模式和存储模式, 最后定义 token的属性。
2.WebSecurityConfigurerAdapter
对应 定义ouath2的配置
加密bean
供项目中加密用
/**
* @MethodName: passwordEncoder
* @Description: 密码加密bean
* @Param: []
* @Return: org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
* @Author: pl
* @Date: 21:14
**/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
// 配置默认的加密方式
return new BCryptPasswordEncoder();
}
定义内存中的用户和初始化用户
/**
* @MethodName: userDetailsService
* @Description: 这个bean用于为内存提供初始化用户
* @Param: []
* @Return: org.springframework.security.core.userdetails.UserDetailsService
* @Author: pl
* @Date: 21:05
**/
@Bean
@Override
protected UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
用户
/**
* @Auther: pl
* @Date: 2020/3/22 20:26
* @Description:内存模式用户
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private static final String USERNAME = "admin";
private static final String PASSWORD = "$2a$10$WhCuqmyCsYdqtJvM0/J4seCU.xZQHe2snNE5VFUuBGUZWPbtdl3GG";
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
// 用户名匹配
if (userName.equals(USERNAME)) {
List<GrantedAuthority> grantedAuthorities = Lists.newArrayList();
//授权USER角色 随便定义的角色
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("USER");
grantedAuthorities.add(grantedAuthority);
return new User(USERNAME, PASSWORD, grantedAuthorities);
}
// 用户名不匹配
else {
return null;
}
}
}
/**
* @Auther: pl
* @Date: 2020/3/22 20:26
* @Description:jdbc模式用户
*/
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private TbUserServiceImpl tbUserService;
@Autowired
private TbPermissionService tbPermissionService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
TbUser user = tbUserService.getUserByUserName(s);
ArrayList<GrantedAuthority> grantedAuthorities = Lists.newArrayList();
if (user!=null){
List<TbPermission> tbPermissions = tbPermissionService.selectByUserId(user.getId());
tbPermissions.forEach(tbPermission -> {
SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(tbPermission.getEnname());
grantedAuthorities.add(grantedAuthority);
});
System.out.println("+++++++++++++++++++++++++++++++");
return new User(user.getUsername(),user.getPassword(),grantedAuthorities);
}
return null;
}
}
初始化用户
/**
* @MethodName: configure
* @Description: configure方法重写,用来增强。此处用于内存中初始化用户
* @Param: [auth]
* @Return: void
* @Author: pl
* @Date: 21:07
**/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
定义oauth2模式
/**
* 用于支持 password 模式 ,ouath2有四种模式,本项目采用密码模式。
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
忽略拦截
/**
* @MethodName: configure
* @Description: 忽略拦截/user/login
* @Param: [web]
* @Return: void
* @Author: pl
* @Date: 23:16
**/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/user/login");
}
授权访问路径
/**
* @MethodName: configure
* @Description: 授权
* @Param: [http]
* @Return: void
* @Author: pl
* @Date: 0:03
**/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 增加了授权访问配置 hasAuthority定义那些角色有这个路径访问权
.antMatchers("/user/info").hasAuthority("USER")
.antMatchers("/user/logout").hasAuthority("USER");
}