目录
1、Oauth2 相关的数据库
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(255) DEFAULT NULL,
`token` blob,
`authentication_id` varchar(255) DEFAULT NULL,
`user_name` varchar(255) DEFAULT NULL,
`client_id` varchar(255) DEFAULT NULL,
`authentication` blob,
`refresh_token` varchar(255) DEFAULT NULL,
KEY `token_id_index` (`token_id`),
KEY `authentication_id_index` (`authentication_id`),
KEY `user_name_index` (`user_name`),
KEY `client_id_index` (`client_id`),
KEY `refresh_token_index` (`refresh_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(255) NOT NULL,
`client_secret` varchar(255) DEFAULT NULL,
`resource_ids` varchar(255) DEFAULT NULL,
`scope` varchar(255) DEFAULT NULL,
`authorized_grant_types` varchar(255) DEFAULT NULL,
`web_server_redirect_uri` varchar(255) DEFAULT NULL,
`authorities` varchar(255) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` text,
`autoapprove` varchar(255) DEFAULT 'false',
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `oauth_client_details`(`client_id`,`client_secret`,`resource_ids`,`scope`,`authorized_grant_types`,`web_server_redirect_uri`,`authorities`,`access_token_validity`,`refresh_token_validity`,`additional_information`,`autoapprove`) values ('client1','$2a$10$cYYV9YSQXQPcLT.sPlgZQeAe7N7k50ktWJBAm.jNLeiPrtJLYYVRC','my-resource','access_token','client_credentials,refresh_token',NULL,'USER',43200,43200,NULL,'false'),('client2','$2a$10$dXV2c1BDGI7ALdQa/lR.hOxCrW5OD.hDM6NXDj0KSWaP5wd9DH1MS','my-resource','access_token','password,refresh_token',NULL,'USER',43200,43200,NULL,'false');
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
`code` varchar(255) DEFAULT NULL,
`authentication` blob,
KEY `code_index` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(255) DEFAULT NULL,
`token` blob,
`authentication` blob,
KEY `token_id_index` (`token_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3、yml配置
server:
port: 9101
tomcat:
uri-encoding: UTF-8
max-threads: 1000
min-spare-threads: 30
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/oauth2?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
redis:
host: redis-host
port: 6379
2、依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.11.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
4、实体类
/**
* @author zhe.xiao
* @date 2021-03-27 13:09
* @description
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class MyUser {
private Long id;
private String username;
private String password;
private Integer phone;
private Boolean active;
}
5、controller接口测试
@RestController
@RequestMapping("/goods")
public class GoodsController {
@GetMapping("/list")
public String list(){
return "商品列表";
}
@GetMapping("/add")
public String add(){
return "添加商品成功";
}
@GetMapping("/update")
public String update(){
return "修改商品成功";
}
@GetMapping("/delete")
public String delete(){
return "删除商品成功";
}
}
6、实现UserDetailsService接口
@Service
public class UserService implements UserDetailsService {
@Autowired
PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MyUser myUser = getUser(username);
return new User(
myUser.getUsername(),
myUser.getPassword(),
myUser.getActive(),
true,
true,
true,
getAuthority(myUser)
);
}
/**
* 模拟 从数据库读取的用户
*
* @param username
* @return
*/
private MyUser getUser(String username) {
return new MyUser()
.setUsername(username)
.setId(1L)
.setPassword(passwordEncoder.encode("123"))
.setActive(true);
}
/**
* 模拟 读取到的用户权限
*
* @param myUser
* @return
*/
private List<GrantedAuthority> getAuthority(MyUser myUser) {
List<GrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return list;
}
}
7、secrity配置
/**
* @author zhe.xiao
* @date 2021-03-26 15:46
* @description
* OAUTH2 第一步配置 web
*/
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
UserService userDetailsService;
/**
* 认证管理器
* OAUTH2 认证服务器会使用这个
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 用户验证
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
/**
* HTTP请求权限
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/goods/list").permitAll()
.antMatchers("/goods/add").permitAll()
.antMatchers("/goods/update").permitAll()
.antMatchers("/goods/delete").permitAll()
.antMatchers("/oauth/**").permitAll()
.antMatchers("/api/**").authenticated();
}
}
8、Oauth2认证服务器配置
/**
* @author zhe.xiao
* @date 2021-03-26 15:23
* @description Oauth2 第三步 配置认证服务器
*/
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String JWT_SINGING_KEY = "my-sign-key-xxxxxx";
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
DataSource dataSource;
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserService userDetailsService;
/**
* 设置第三方client相关的数据信息
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService());
}
/**
* 设定一些与认证相关的配置
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()) //配置token存储信息
.accessTokenConverter(jwtAccessTokenConverter()) //配置jwt
.authenticationManager(authenticationManager) //配置认证
.userDetailsService(userDetailsService) //配置用户信息
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); //支持OAUTH相关接口以GET和POST方法请求
}
/**
* 一些服务器配置
*
* @param oauthServer
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients() //支持请求数据通过表单的形式发送
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()");
}
/**
* 数据库存储token信息
* 主要表:oauth_access_token、oauth_refresh_token
*
* @return
*/
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
/**
* 数据库读取client的相关数据
* 主要表:oauth_client_details
*
* 注意:这里需要重命名bean name,否则会提示 clientDetailsService 无法被override
* @return
*/
@Bean(name = "oauthClientDetailsService")
public JdbcClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
/**
* 生成JWT
*
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(JWT_SINGING_KEY);
return converter;
}
}
9、Oauth2资源服务器配置
/**
* @author zhe.xiao
* @date 2021-03-26 15:35
* @description
* Oauth2 第二步 配置资源服务器
*/
@EnableResourceServer
@Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
public static final String RESOURCE_NAME = "my-resource";
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//只有基于token的认证才可以访问这些资源
resources.resourceId(RESOURCE_NAME).stateless(true);
}
}
10、测试结果:
10.1、client_credentials认证:
请求路径: http://localhost:9101/oauth/token
参数:
grant_type :client_credentials
scope :access_token
client_id :client1
client_secret :client1_secret
10.2 密码认证:
请求路径: http://localhost:9101/oauth/token
参数:
grant_type :password
scope :access_token
client_id :client2
client_secret :client2_secret
username :admin
password:123
10.3 检查token是否合法
postman有两种方法带token
请求路径:http://localhost:9101/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibXktcmVzb3VyY2UiXSwidXNlcl9uYW1lIjoidGVzdCIsInNjb3BlIjpbImFjY2Vzc190b2tlbiJdLCJleHAiOjE2MzIwODA3NTEsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZDk5NjY0MDktZDE3Ni00ZjNhLWFjNTItODk2NTY1MmVjNjkxIiwiY2xpZW50X2lkIjoiY2xpZW50MiJ9.6hRekdtvPzfDdKxP9b8E7CAWGB6PYXkqJuJ5VKvtRjk
第一种:url拼接
第二种:Authorzation的bearer 携带token
以上两种方法都可以
10.4 6.4 通过token访问controller
没带token:
带了token
没有oauth_client_details表会出现以下错误:
其他的没有表的情况下都是以下错误: