spring"/>
OAuth2与spring
OAuth2与spring-security-oauth2
- OAuth2
- OAuth2是什么
- OAuth2中4种授权模式作用场景
- spring-security-oauth2
- spring-security-oauth2是什么
- 搭建spring-security-oauth2案例
- 环境版本
- pom.xml
- application.yml
- 目录结构
- 密码模式
- 获取token
- 访问受保护的资源
- 授权码模式
- 获取code
- 获取token
- 访问受保护的资源
- 结语
OAuth2
OAuth2是什么
关于这块概念可以参考:
OAuth2中4种授权模式作用场景
- 授权码模式
授权码模式一般用于提供给第三方使用 - 简化模式
简化模式一般不会使用 - 密码模式
密码模式一般仅用于系统内部使用 - 客户端凭证模式
客户端凭证模式一般不会使用
spring-security-oauth2
spring-security-oauth2是什么
spring-security-oauth2是基于spring-security框架完整实现oauth2协议的框架,具有oauth2中4种模式访问和第三方登录等功能。
搭建spring-security-oauth2案例
环境版本
<properties><java.version>1.8</java.version><spring.cloud.version>Hoxton.SR9</spring.cloud.version><spring.boot.version>2.3.0.RELEASE</spring.boot.version><mysql.connector.version>8.0.15</mysql.connector.version></properties>
pom.xml
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.1.0.RELEASE</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.connector.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring.boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring.cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
application.yml
server:port: 8080spring:datasource:url: jdbc:mysql://192.168.174.129:3306/oauth?serverTimezone=GMT%2B8&characterEncoding=utf-8&autoReconnect=true&allowMultiQueries=trueusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
目录结构
DemoAuthorizationServerConfiguration:授权服务配置
@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
public class DemoAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {private DataSource dataSource;private TokenStore tokenStore;private AuthenticationManager authenticationManager;private UserDetailsService userDetailsService;private TokenEnhancer tokenEnhancer;private JwtAccessTokenConverter jwtAccessTokenConverter;@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// @formatter:offclients.inMemory().withClient("test-client").secret("$2a$08$YGw560YLRWHg3Hl29ZlmdOfAeyRQ2u0kDiqUyQ62Y1pkW5n4a.hjO").authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit").authorities("ROLE_CLIENT").redirectUris("http://localhost:8084/oauth/callback").scopes("read", "write", "all");// 请求参数scope必须为集合中的某个值// @formatter:on}@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {// 允许表单传参client信息security.allowFormAuthenticationForClients().tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.tokenStore(tokenStore).authenticationManager(authenticationManager).userDetailsService(userDetailsService).tokenEnhancer(tokenEnhancer).accessTokenConverter(jwtAccessTokenConverter);}}
DemoJwtTokenStoreConfiguration:JwtToken仓库配置
@Configuration
public class DemoJwtTokenStoreConfiguration {@Beanpublic TokenStore tokenStore(JwtAccessTokenConverter tokenConverter) {return new JwtTokenStore(tokenConverter);}@Beanpublic JwtAccessTokenConverter tokenConverter() {JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();tokenConverter.setSigningKey("abc");return tokenConverter;}@Beanpublic TokenEnhancer tokenEnhancer(JwtAccessTokenConverter tokenConverter) {return new DemoJwtTokenEnhancer(tokenConverter);}}
DemoResourceServerConfiguration:资源服务配置
@Configuration
@EnableResourceServer
@AllArgsConstructor
public class DemoResourceServerConfiguration extends ResourceServerConfigurerAdapter {private AccessDeniedHandler accessDeniedHandler;// private AuthenticationEntryPoint authenticationEntryPoint;@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().exceptionHandling().accessDeniedHandler(accessDeniedHandler)
// .authenticationEntryPoint(authenticationEntryPoint).and().csrf().disable();}@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {super.configure(resources);}}
DemoWebSecurityConfiguration:WebSecurity配置
@Configuration
public class DemoWebSecurityConfiguration extends WebSecurityConfigurerAdapter {@Bean@Override@SneakyThrowspublic AuthenticationManager authenticationManagerBean() {return super.authenticationManagerBean();}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A, 8);}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.httpBasic().and().csrf().disable();}
}
DemoController:受保护的Controller资源
@RestController
@RequestMapping("/api")
public class DemoController {@RequestMapping("/test")public Map<String, Object> test() {Map<String, Object> result = new HashMap<>();result.put("time", System.currentTimeMillis());return result;}}
DemoClientDetailsService:ClientDetailsService实现类
public class DemoClientDetailsService extends JdbcClientDetailsService {public DemoClientDetailsService(DataSource dataSource) {super(dataSource);}}
DemoUserDetails:用户信息类
@Getter
public class DemoUserDetails extends User {/*** 用户id*/private String userId;/*** 手机号*/private String mobile;/*** 是否超级管理员 1是 0否*/private Boolean administrator;/*** 角色id*/private List<Long> roles;public DemoUserDetails(String userId, String mobile, String username, String password, boolean enabled, List<Long> roles, boolean accountNonExpired,boolean credentialsNonExpired, boolean accountNonLocked,Collection<? extends GrantedAuthority> authorities, Boolean administrator) {super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked,authorities);this.userId = userId;this.roles = roles;this.administrator = administrator;this.mobile = mobile;}}
DemoUserDetailsServiceImpl:UserDetailsService实现类
@Service
public class DemoUserDetailsServiceImpl implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {return new DemoUserDetails("userId", "mobile", s, "$2a$08$EiGfaup8QkkjT6DhvCSFRuNhdFTEV7Rbu/avUm8lGL2ZUji/lTWji", true, Arrays.asList(1L), true, true, true,AuthorityUtilsmaSeparatedStringToAuthorityList("1,2"), false);}}
DemoAccessDeniedHandler:403处理器
@Component
public class DemoAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);httpServletResponse.setContentType("application/json;charset=UTF-8");PrintWriter out = httpServletResponse.getWriter();out.write(new ObjectMapper().writeValueAsString("权限不足,请联系管理员!"));out.flush();out.close();}}
DemoAuthenticationEntryPoint:401处理器
//@Component
public class DemoAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);httpServletResponse.setContentType("application/json;charset=UTF-8");PrintWriter out = httpServletResponse.getWriter();out.write(new ObjectMapper().writeValueAsString("请先登录!"));out.flush();out.close();}}
DemoJwtTokenEnhancer:TokenEnhancer实现类
@AllArgsConstructor
public class DemoJwtTokenEnhancer implements TokenEnhancer {private JwtAccessTokenConverter jwtAccessTokenConverter;@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {DemoUserDetails principal =(DemoUserDetails) authentication.getUserAuthentication().getPrincipal();Map<String, Object> info = new HashMap<>();info.put("user_id", principal.getUserId() == null ? "" : String.valueOf(principal.getUserId()));((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);return jwtAccessTokenConverter.enhance(accessToken, authentication);}}
DemoPasswordEncoder:PasswordEncoder实现类
public class DemoPasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence rawPassword) {return (String) rawPassword;}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {return encodedPassword.equals(encode(rawPassword));}}
密码模式
获取token
/oauth/token,获取token,access_token就是我们需要在请求头中携带的token。
访问url:
http://localhost:8080/oauth/token?username=admin&password=qwer1234&grant_type=password&scope=all
并在请求头中加入basic auth参数
眼力好的同学已经看出来上图中已经在url中出现了client…
也可以直接访问这个url:
http://localhost:8080/oauth/token?username=admin&password=qwer1234&grant_type=password&scope=all&client_id=test-client&client_secret=abc
这种访问方式又是哪里出来的?第一个java代码块里面就有配置了…
allowFormAuthenticationForClients又是怎么实现的呢?
@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {// 允许表单传参client信息security.allowFormAuthenticationForClients().tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");}
访问受保护的资源
访问受保护的资源
图片中就是在请求头中将入key为Authorization的值
Authorization Bearer eyJhbGciOiJ…
授权码模式
获取code
直接在浏览器上访问该链接
http://localhost:8080/oauth/authorize?response_type=code&scope=all&client_id=test-client&client_secret=abc&redirect_uri=http://localhost:8084/oauth/callback
浏览器弹出httpBasic验证
输入预设用户名/密码:admin/qwer1234
点击确定,弹出OAuth2用户授权页面,点击授权Authorize
页面重定向到redirect_uri参数指定的url并附带了code
获取token
使用上一步中获取的code替换下面的,并访问url:
http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=test-client&client_secret=abc&redirect_uri=http://localhost:8084/oauth/callback&code=OWwAmR
不会有人直接复制链接不改code参数吧?不会吧,不会吧
访问受保护的资源
该步骤与密码模式中一致
结语
spring-security-oauth2秉承spring一贯风格特色,配置多种多样,如果见到和上面配置不一样很正常,如果想要深入了解,需要仔细阅读源码。上面的案例只是简单的入门描述,由此可以引发很多疑问。
- 使用数据库配置client与user怎么做
- 需要配置手机号验证码登录等其他方式需要怎么做
- 授权码模式需要用户手动授权,如果特殊情况下改为不需要授权怎么做
- 需要在网关中使用,该怎么做
- 需要权限验证,该怎么做
- 基于spring-security-oauth2框架的单点登录怎么做
…
关注点赞,下次继续更新
配置篇 spring-security-oauth2 配置(一)
源码分析篇A路线 spring-security-oauth2 源码分析A路线(一)
源码分析篇B路线 spring-security-oauth2 源码分析B路线(一)
更多推荐
OAuth2与spring
发布评论