一、基本使用
1.1 添加依赖
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security Oauth2 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
启动输出:
1.2 登录
username:user
password:4757819a-077e-42bb-a589-0e8322b824f6
1.3 常用配置
WebSecurityConfiguration.java
package com.qiang.security.oauth2.code.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @author: 吴多强
* @create: 2020-09-08 23:33
* @description: 服务器安全配置
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* 配置默认的加密方式
*
* @return
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 在内存创建用户
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
// 使用内存存储密码
.inMemoryAuthentication()
// 添加一个管理员用户,authorities拥有的资源访问权限
.withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN")
.and()
// 添加一个普通用户,authorities拥有的资源访问权限
.withUser("qiang").password(passwordEncoder().encode("123456")).roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 指定角色有访问的资源权限
.antMatchers("/resource/one").hasRole("USER")
.antMatchers("/resource/two").hasRole("ADMIN")
// 请求为/user/**的需要认证
.antMatchers("/user/**").authenticated()
// 请求不为/user/**的不需要认证
.anyRequest().permitAll()
.and()
// 允许表单登录
.formLogin()
// 登录成功后跳转的地址
.successForwardUrl("/user/success");
}
}
表单参数
protected void configure(HttpSecurity http) throws Exception {
http
// 若要给应用程序发送请求,则发送请求的用户必须先通过认证
.authorizeRequests()
.anyRequest().authenticated()
.and()
// 允许用户采用表单登录的方式进行认证
.formLogin()
// 重写登录页面
.loginPage("/login")
// 登录成功后跳转的页面,必须是Post请求的
.successForwardUrl("/oauth/login/success")
.and()
// 允许用户采用HTTP基本的认证方式进行认证
.httpBasic();
}
上述代码是默认的配置,规定了一下三点:
- 若要给应用程序发送请求,则发送请求的用户必须先通过认证。
- 允许用户采用表单登录的方式进行认证。
- 允许用户采用HTTP基本的认证方式进行认证。
资源设置
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
http.authorizeRequests()
下添加了多个匹配器,每个匹配器用来控制不同的URL接受不同的用户访问。简单讲,http.authorizeRequests()
就是在进行请求的权限配置。- 所有用户都可以访问以/resources/**开头的URL,和
/signup
、/about
两个URL。 - 拥有
ADMIN
角色的用户可以访问以/admin/
开头的URL。hasRole(String)
:如果当前用户有String
表示的角色,则返回True
。 - 同时拥有
ADMIN
和DBA
角色的用户可以访问以/db/**
开头的URL。access(String)
:当String
为true
时才可进行访问。 - 所有没被匹配器匹配到的URL都需用户通过认证。
and()
返回一个SecurityBuilder
。Spring Security
支持两种认证方式:formLogin()
和httpBasic()
。
1.4 常用路径
# 登录
http://localhost:8002/login
# 登出
http://localhost:8002/logout
# 获取token
http://localhost:8002/oauth/token
二、简化模式
2.1 服务器安全配置
package com.qiang.security.oauth2.simple.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @author: 吴多强
* @create: 2020-09-08 23:33
* @description: 服务器安全配置
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* 配置默认的加密方式
*
* @return
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 在内存创建用户
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
// 使用内存存储密码
.inMemoryAuthentication()
// 添加一个管理员用户,authorities拥有的资源访问权限
.withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN")
.and()
// 添加一个普通用户,authorities拥有的资源访问权限
.withUser("qiang").password(passwordEncoder().encode("123456")).roles("USER");
}
}
2.2 认证服务器配置
package com.qiang.security.oauth2.simple.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
/**
* @author: 吴多强
* @create: 2020-09-10 00:51
* @description:
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.
inMemory()
.withClient("client")
.secret(new BCryptPasswordEncoder().encode("secret"))
// 授权模式,implicit简化模式
.authorizedGrantTypes("authorization_code","implicit")
.scopes("app")
.redirectUris("www.wuduoqiang.com");
}
}
2.3 获取token
访问:
response_type=token 说明是简化模式。
是否授权。
拿到token。
三、授权码模式
3.1 服务器安全配置
package com.qiang.security.oauth2.code.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @author: 吴多强
* @create: 2020-09-08 23:33
* @description: 服务器安全配置
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* 配置默认的加密方式
*
* @return
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 在内存创建用户
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
// 使用内存存储密码
.inMemoryAuthentication()
// 添加一个管理员用户,authorities拥有的资源访问权限
.withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN")
.and()
// 添加一个普通用户,authorities拥有的资源访问权限
.withUser("qiang").password(passwordEncoder().encode("123456")).roles("USER");
}
}
3.2 认证服务器配置
package com.qiang.security.oauth2.code.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
/**
* @author: 吴多强
* @create: 2020-09-08 23:34
* @description: 配置认证服务器
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置客户端
clients
// 使用内存设置
.inMemory()
// client_id
.withClient("client")
// client_secret
.secret(passwordEncoder.encode("secret"))
// 授权类型
.authorizedGrantTypes("authorization_code")
// 授权范围
.scopes("app")
// 注册回调地址
.redirectUris("https://www.baidu.com");
}
}
3.3 获取token
获取授权码。
http://localhost:8002/oauth/authorize?client_id=client&response_type=code
第一次会自动跳转到登录。
http://localhost:8002/login
验证成功后会询问用户是否授权客户端。
选择授权后会得到一个code。
然后使用code获取token。
http://client:secret@localhost:8002/oauth/token?grant_type=authorization_code&code=r2i0JK
四、客户端模式
4.1 认证服务器配置
package com.qiang.security.oauth2.client.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
/**
* @author: 吴多强
* @create: 2020-09-09 22:45
* @description:
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置客户端
clients
// 使用内存设置
.inMemory()
// client_id
.withClient("client")
// client_secret
.secret(passwordEncoder().encode("secret"))
// 授权类型
.authorizedGrantTypes("client_credentials")
// 授权范围
.scopes("app")
// 注册回调地址
.redirectUris("https://www.baidu.com");
}
}
4.2 获取token
五、密码模式
5.1 自定义认证授权
实现spring security oauth2 的UserDetailsService接口,自定义认证授权。
package com.qiang.security.oauth2.password.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.stereotype.Service;
/**
* @author: 吴多强
* @create: 2020-09-09 21:37
* @description: 实现spring security 的用户接口,自定义认证授权
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
/**
* 根据用户名获取用户的角色、权限等信息
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("用户名:" + username);
// 当用户名为qiang的时候才构建UserDetails,后面需要从数据库查询
if ("qiang".equals(username)) {
return new User(username, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
}
return null;
}
}
5.2 服务器安全配置
package com.qiang.security.oauth2.password.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @author: 吴多强
* @create: 2020-09-09 21:59
* @description: 认证服务器安全配置
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
// 配置默认的加密方式
return new BCryptPasswordEncoder();
}
/**
* 使用自定义的用户认证授权
* @return
*/
@Bean
@Override
protected UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
/**
* 配置自定义的用户认证授权
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
/**
* 用于支持 password 模式
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
5.3 认证服务器配置
package com.qiang.security.oauth2.password.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
/**
* @author: 吴多强
* @create: 2020-09-09 22:04
* @description:
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
/**
* 注入用于支持 password 模式
*/
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 用于支持密码模式
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 允许客户端访问 /oauth/check_token 检查 token
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
/**
* 配置客户端
*
* @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")
// 授权范围
.scopes("backend")
// 可以设置对哪些资源有访问权限,不设置则全部资源都可以访问
.resourceIds("backend-resources")
// 设置访问令牌的有效期,这里是 1 天
.accessTokenValiditySeconds(60 * 60 * 24)
// 设置刷新令牌的有效期,这里是 30 天
.refreshTokenValiditySeconds(60 * 60 * 24 * 30);
}
}
5.4 获取token
http://localhost:8003/oauth/token
username:qiang
password:123456
grant_type:password
client_id:client
client_secret:secret
六、常用注解
@EnableGlobalMethodSecurity
全局方法拦截
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
(1)@EnableGlobalMethodSecurity(securedEnabled=true) ,开启@Secured 注解过滤权限。
(2)@EnableGlobalMethodSecurity(jsr250Enabled=true),开启@RolesAllowed 注解过滤权限 。
(3)@EnableGlobalMethodSecurity(prePostEnabled=true) ,使用表达式时间方法级别的安全性。
@PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问
@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
@PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果
@PreFilter 允许方法调用,但必须在进入方法之前过滤输入值
例如下面代码就表示如果用户具有admin角色,就能访问listAllUsers方法,但是如果方法前不加@preAuthorize注解,意味着所有用户都能访问listAllUsers。
@PreAuthorize("hasRole(‘admin‘)")
@RequestMapping(value = "/user/", method = RequestMethod.GET)
@ResponseBody
publicList listAllUsers() {
……
}