4.0.0
oauth-learn
oauth-parent
1.0-SNAPSHOT
pom
org.springframework.boot
spring-boot-starter-parent
2.1.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.cloud
spring-cloud-dependencies
Greenwich.RELEASE
pom
import
javax.servlet
javax.servlet-api
3.1.0
provided
javax.interceptor
javax.interceptor-api
1.2
com.alibaba
fastjson
1.2.47
org.projectlombok
lombok
1.18.0
org.springframework.security
spring-security-jwt
1.0.10.RELEASE
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.1.3.RELEASE
org.apache.maven.plugins
maven-compiler-plugin
1.8
maven-resources-plugin
utf-8
true
package com.feizhou.oauth.bean;
import lombok.Data;
/**
* 描述该类- JPA
*
* @author zhoufei
* @class: UserBean
* @date 2020/10/27 16:09
* @Verson 1.0 -2020/10/27 16:09
* @see
*/
@Data
public class UserAuthorizeBean {
private Long id;
private Long userId;
private String authorizeCode;
}
package com.feizhou.oauth.bean;
import lombok.Data;
/**
* 描述该类- JPA
*
* @author zhoufei
* @class: UserBean
* @date 2020/10/27 16:09
* @Verson 1.0 -2020/10/27 16:09
* @see
*/
@Data
public class UserBean {
private Long id;
private String userName;
private String password;
}
package com.feizhou.oauth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
/**
* 描述该类- JPA
*
* @author zhoufei
* @class: AuthorizationServer
* @date 2020/10/28 21:26
* @Verson 1.0 -2020/10/28 21:26
* @see
*/
@Configuration
@EnableAuthorizationServer // 配置授权服务。
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
// 用来配置客户端详情服务
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 这里是第三方合作用户的客户id,秘钥的配置
// 使用in-memory存储
clients.inMemory()
// client_id,用户账号
.withClient("c1")
// 客户端密钥
.secret(new BCryptPasswordEncoder().encode("secret"))
// 资源列表,资源标识
.resourceIds("res1")
// 授权类型(4种)
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit",
"refresh_token")
// 客戶端允许的授权范围
.scopes("all")
// false跳转到授权页面,让用户点击授权,如果是true,相当于自动点击授权,就不跳转授权页面
.autoApprove(false)//
// 加上验证回调地址,返回授权码信息
.redirectUris("http://www.baidu.com");
// 如果有多个用户,配置多个客户详情
// .and().withClient()
}
@Autowired
// 令牌存储策略
private TokenStore tokenStore;
@Autowired
// 客户端详情服务,也就是configure(ClientDetailsServiceConfigurer clients)方法
private ClientDetailsService clientDetailsService;
// 令牌管理服务
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);// 支持刷新
service.setTokenStore(tokenStore);// 令牌存储
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
return service;
}
@Autowired
// 授权码服务
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
// 认证管理
private AuthenticationManager authenticationManager;
@Autowired
// 令牌管理服务
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Override
// 用来配置令牌(token)的访问端点
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
// 密码模式需要
.authenticationManager(authenticationManager)
// 授权码模式需要
.authorizationCodeServices(authorizationCodeServices)
// 令牌管理服务
.tokenServices(authorizationServerTokenServices).allowedTokenEndpointRequestMethods(HttpMethod.POST);// 允许post提交
}
@Bean
// 授权码服务器
public AuthorizationCodeServices authorizationCodeServices() {
// 授权码模式的授权码采用内存方式存储
return new InMemoryAuthorizationCodeServices();
}
@Override
// 用来配置令牌端点的安全约束,拦截规则
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 提供公有密匙的端点,如果你使用JWT令牌的话, 允许
.tokenKeyAccess("permitAll()")
// oauth/check_token:用于资源服务访问的令牌解析端点,允许
.checkTokenAccess("permitAll()")
// 表单认证,申请令牌
.allowFormAuthenticationForClients();
}
@Bean
// 令牌存储策略
public TokenStore tokenStore() {
// 内存存储,普通令牌
return new InMemoryTokenStore();
}
}
package com.feizhou.oauth.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.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author Administrator
* @version 1.0
**/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 认证管理器
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
// 密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
csrf().disable().
authorizeRequests().
antMatchers("/admin/p1").
hasAnyAuthority("p1").
antMatchers("/user/p2").
hasAnyAuthority("p2").
antMatchers("/login*")
.permitAll().
anyRequest().
authenticated().
and().
formLogin();
}
}
package com.feizhou.oauth.dao;
import java.util.ArrayList;
import java.util.List;
import com.feizhou.oauth.bean.UserAuthorizeBean;
import com.feizhou.oauth.bean.UserBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
/**
* 描述该类- JPA
*
* @author zhoufei
* @class: UserBean
* @date 2020/10/27 16:09
* @Verson 1.0 -2020/10/27 16:09
* @see
*/
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
// 根据账号查询用户信息
public UserBean getUserByUsername(String username) {
String sql = "select * from user where user_name = ?";
// 连接数据库查询用户
List list = jdbcTemplate.query(sql, new Object[] { username },
new BeanPropertyRowMapper<>(UserBean.class));
if (list != null && list.size() == 1) {
return list.get(0);
}
return null;
}
// 根据用户id查询用户权限
public List getAuthorize(Long userId) {
String sql = "SELECT * FROM user_authorize WHERE user_id =?";
List list = jdbcTemplate.query(sql, new Object[] { userId },
new BeanPropertyRowMapper<>(UserAuthorizeBean.class));
List authorizes = new ArrayList<>();
list.forEach(c -> authorizes.add(c.getAuthorizeCode()));
return authorizes;
}
}
package com.feizhou.oauth.service;
import java.util.List;
import com.feizhou.oauth.bean.UserBean;
import com.feizhou.oauth.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
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.stereotype.Service;
/**
* @author Administrator
* @version 1.0
**/
@Service
public class SpringDataUserDetailsService implements UserDetailsService {
@Autowired
UserDao userDao;
// 根据账号查询用户信息,
// 通过@Service将SpringDataUserDetailsService注入容器,通过UserDetailsService接口表明该类的类型是UserDetailsService
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 将来连接数据库根据账号查询用户信息
UserBean bean = userDao.getUserByUsername(username);
if (bean == null) {
// 如果用户查不到,返回null,由provider来抛出异常
return null;
}
// 查询当前数据库的用户资源权限
List authorize = userDao.getAuthorize(bean.getId());
String[] authorizeArr = new String[authorize.size()];
authorize.toArray(authorizeArr);
// 添加权限
UserDetails userDetails = User.withUsername(bean.getUserName()).password(bean.getPassword())
.authorities(authorizeArr).build();
return userDetails;
}
}
spring.application.name=oauth-authorizationServer
server.port=8081
spring.datasource.url = jdbc:mysql://zhoufei.ali.db.com:3306/test?useUnicode=true
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
oauth-parent
oauth-learn
1.0-SNAPSHOT
4.0.0
oauth-authorizationServer
org.springframework.boot
spring-boot-starter-web
org.springframework.data
spring-data-commons
org.springframework.cloud
spring-cloud-starter-security
org.springframework.cloud
spring-cloud-starter-oauth2
org.springframework.security
spring-security-jwt
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-jdbc
org.projectlombok
lombok
clientId:标识客户的Id
secret:客户端秘钥
scope:限制客户端的访问范围
authorizedGrantTypes:此客户端可以使用的授权类型
authorities:客户端可以使用的权限
默认实现:InMemoryTokenStore
JdbcTokenStore
JwtTokenStore
令牌端点的安全约束
tokenkey:/oauth/token_key端点完全公开。
checkToken:/oauth/check_token端点完全公开。
allowFormAuthenticationForClients:允许表单认证
具体代码
http://localhost:8081/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=kZAlHW&redirect_uri=http://www.baidu.com
http://localhost:8081/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com
http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=admin&password=admin
http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials
package com.feizhou.oauth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
/**
* @author Administrator
* @version 1.0
**/
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
// 授权服务的资源列表
public static final String RESOURCE_ID = "res1";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
// 资源 id
.resourceId(RESOURCE_ID)
// 令牌服务
.tokenServices(tokenService())
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/**")
// 所有的访问,授权访问都要是all,和认证服务器的授权范围一一对应
.access("#oauth2.hasScope('all')")
//去掉防跨域攻击
.and().csrf().disable()
//session管理
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
// 资源服务令牌解析服务
public ResourceServerTokenServices tokenService() {
// 使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
RemoteTokenServices service = new RemoteTokenServices();
service.setCheckTokenEndpointUrl("http://localhost:8081/oauth/check_token");
service.setClientId("c1");
service.setClientSecret("secret");
return service;
}
}
package com.feizhou.oauth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
//由于Spring boot starter自动装配机制,这里无需使用@EnableWebSecurity
//@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 方法授权
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 密码编码器,不加密
@Bean
public PasswordEncoder passwordEncoder() {
// return NoOpPasswordEncoder.getInstance(); // 不加密
return new BCryptPasswordEncoder();// BCryptPasswordEncoder加密
}
// web url 拦截规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()// 所有其他请求必须认证通过
.and().formLogin().loginPage("/login").successForwardUrl("/login-success")// 自定义登录成功的页面地址
.permitAll().and().logout().permitAll().and()// 会话管理
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);// 会话管理
}
}
package com.feizhou.oauth.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Administrator
* @version 1.0
**/
@RestController
public class LoginController {
@RequestMapping(value = "/login-success")
public String loginSuccess() {
return getUsername() + " login-success 登录成功";
}
/**
* 测试资源1
*
* @return
*/
@GetMapping(value = "/admin/p1")
@PreAuthorize("hasAuthority('p1')")//拥有p1权限才可以访问
public String r1() {
return " /admin/p1 " + getUsername() + "访问资源1";
}
/**
* 测试资源2
*
* @return
*/
@GetMapping(value = "/user/p2")
@PreAuthorize("hasAuthority('p2')")//拥有p2权限才可以访问
public String r2() {
return "/user/p2 " + getUsername() + "访问资源2";
}
// 获取当前用户信息
private String getUsername() {
String username = null;
// 当前认证通过的用户身份
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 用户身份
Object principal = authentication.getPrincipal();
if (principal == null) {
username = "匿名";
}
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal;
username = userDetails.getUsername();
} else {
username = principal.toString();
}
return username;
}
}
spring.application.name=oauth-ResourceServer
server.port=8082
oauth-parent
oauth-learn
1.0-SNAPSHOT
4.0.0
oauth-ResourceServer
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-security
org.springframework.cloud
spring-cloud-starter-oauth2
org.projectlombok
lombok
tokenServices:
tokenStore:
resourceId
tokenExtractor
DefaultTokenServices
RemoteTokenServices
代码文章
https://gitee.com/DanShenGuiZu/learnDemo/tree/mysql_mybaties_DB/oauth2.0-learn