oauth2.0--基础--05--单点登陆设计

oauth2.0–基础–05–单点登陆设计


代码文章

https://gitee.com/DanShenGuiZu/learnDemo/tree/master/auth2.0--learn/sso/oauth2.0-sso

1、介绍

  1. 框架:spring sercurity+oauth2.0+JWT
  2. 认证服务器和资源服务器在同一台机器(网关)
  3. 密码授权模式
    1. 去掉客户端信息表
    2. 去掉用户授权表

2、代码

2.1、结构

在这里插入图片描述

2.2、代码

UserBean

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;
	
}

AuthorizationServer

package com.feizhou.oauth.config;

import java.util.Arrays;

import javax.sql.DataSource;

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.crypto.password.PasswordEncoder;
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.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * 描述该类- 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 {


	@Autowired
	private JwtAccessTokenConverter accessTokenConverter;
	@Autowired
	// 授权码服务
	private AuthorizationCodeServices authorizationCodeServices;
	@Autowired
	// 认证管理
	private AuthenticationManager authenticationManager;
	@Autowired
	// 令牌管理服务
	private AuthorizationServerTokenServices authorizationServerTokenServices;
	
	@Autowired
	// 令牌存储策略
	private TokenStore tokenStore;
	@Autowired
	// 客户端详情服务,也就是configure(ClientDetailsServiceConfigurer clients)方法
	private ClientDetailsService clientDetailsService;
	
	@Autowired
	// 加密方式
	private PasswordEncoder passwordEncoder;
	
	// 用来配置客户端详情服务
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// 这里是第三方合作用户的客户id,秘钥的配置
		// 使用in-memory存储
		clients.inMemory()
				// client_id,用户账号
				.withClient(OauthCanstant.clientId)
				// 客户端密钥
				.secret(new BCryptPasswordEncoder().encode(OauthCanstant.secret))
				// 资源列表,资源标识
				.resourceIds(OauthCanstant.resourceIds)
				// 授权类型(4种)
				.authorizedGrantTypes(OauthCanstant.authorizedGrantTypes)
				// 客戶端允许的授权范围
				.scopes(OauthCanstant.scopes)
				// false跳转到授权页面,让用户点击授权,如果是true,相当于自动点击授权,就不跳转授权页面
				.autoApprove(OauthCanstant.autoApprove)//
				// 加上验证回调地址,返回授权码信息
				.redirectUris(OauthCanstant.redirectUris);
	}
	
	// 令牌管理服务
	@Bean
	public AuthorizationServerTokenServices tokenService() {
		DefaultTokenServices service = new DefaultTokenServices();
		service.setClientDetailsService(clientDetailsService);
		service.setSupportRefreshToken(true);// 支持刷新
		service.setTokenStore(tokenStore);// 令牌存储
		
		// 令牌增强
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
		service.setTokenEnhancer(tokenEnhancerChain);
		
		service.setAccessTokenValiditySeconds(OauthCanstant.accessTokenValiditySeconds); // 令牌默认有效期2小时
		service.setRefreshTokenValiditySeconds(OauthCanstant.refreshTokenValiditySeconds); // 刷新令牌默认有效期3天
		return service;
	}
	
	@Override
	// 用来配置令牌(token)的访问端点
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		endpoints
				// 指定认证管理器
				.authenticationManager(authenticationManager)
				// 授权码模式需要
				.authorizationCodeServices(authorizationCodeServices)
				// 令牌管理服务
				.tokenServices(authorizationServerTokenServices)
				// jwt格式Token
				.accessTokenConverter(accessTokenConverter)
				// 允许post提交
				.allowedTokenEndpointRequestMethods(HttpMethod.POST);
	}
	
	@Bean
	// 授权码服务器
	public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
		// 授权码模式的授权码采用数据库方式存储
		return new JdbcAuthorizationCodeServices(dataSource);
	}
	
	@Override
	// 用来配置令牌端点的安全约束,拦截规则
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		
		security
				// 提供公有密匙的端点,如果你使用JWT令牌的话, 允许
				.tokenKeyAccess(OauthCanstant.permitAll)
				// oauth/check_token:用于资源服务访问的令牌解析端点,允许
				.checkTokenAccess(OauthCanstant.permitAll)
				// 表单认证,申请令牌
				.allowFormAuthenticationForClients();
	}
	
}

OauthCanstant



package com.feizhou.oauth.config;

/**
 * 描述该类- 客户端常量
 * 
 * @author zhoufei
 * @class: AuthorizationServer
 * @date 2020/10/28 21:26
 * @Verson 1.0 -2020/10/28 21:26
 * @see
 */

public interface OauthCanstant {
	String clientId = "c1";
	String secret = "secret";
	String resourceIds = "res1";
	String authorizedGrantTypes = "password";
	String scopes = "all";
	boolean autoApprove = false;
	String redirectUris = "http://www.baidu.com";
	String permitAll = "permitAll()";
	// 令牌默认有效期2小时
	int accessTokenValiditySeconds = 7200;
	// 刷新令牌默认有效期3天
	int refreshTokenValiditySeconds = 259200;
	
	// 对称秘钥,资源服务器使用该秘钥来验证
	String SIGNING_KEY = "uaa123";
	
}

ResouceServerConfig

package com.feizhou.oauth.config;

import org.springframework.beans.factory.annotation.Autowired;
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.TokenStore;

/**
 * @author Administrator
 * @version 1.0
 **/
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {

	@Autowired
	// 令牌存储策略
	private TokenStore tokenStore;



	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
		resources
				// 资源 id
				.resourceId(OauthCanstant.resourceIds)
//                // 令牌服务
				.tokenStore(tokenStore)
				.stateless(true);
	}
	
	@Override
	public void configure(HttpSecurity http) throws Exception {
		
		http
				
				.authorizeRequests().antMatchers("/**")
				// 所有的访问,授权访问都要是all,和认证服务器的授权范围一一对应
				.access("#oauth2.hasScope('"+OauthCanstant.scopes+"')")
				//去掉防跨域攻击
				.and().csrf().disable()
				//session管理
				.sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}
	

}

TokenConfig


package com.feizhou.oauth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * @author Administrator
 * @version 1.0
 **/
@Configuration
public class TokenConfig {
	

	@Bean
	public TokenStore tokenStore() {
		// JWT令牌存储方案
		return new JwtTokenStore(accessTokenConverter());
	}
	
	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		converter.setSigningKey(OauthCanstant.SIGNING_KEY); // 对称秘钥,资源服务器使用该秘钥来验证
		return converter;
	}
	
}


WebSecurityConfig



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.config.http.SessionCreationPolicy;
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.authorizeRequests().anyRequest().authenticated()// 所有其他请求必须认证通过
				.and().formLogin().loginPage("/login").successForwardUrl("/login-success")// 自定义登录成功的页面地址
				.permitAll().and().logout().permitAll().and()// 会话管理
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);// 会话管理
		
	}
}


LoginController


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;
	}
}

UserDao


package com.feizhou.oauth.dao;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.feizhou.oauth.bean.UserBean;

/**
 * 描述该类- 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<UserBean> list = jdbcTemplate.query(sql, new Object[] { username },
				new BeanPropertyRowMapper<>(UserBean.class));
		if (list != null && list.size() == 1) {
			return list.get(0);
		}
		return null;
	}
	
}

SpringDataUserDetailsService


package com.feizhou.oauth.service;

import java.util.List;

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;

import com.feizhou.oauth.bean.UserBean;
import com.feizhou.oauth.dao.UserDao;

/**
 * @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;
		}
		
		// 添加p1权限,权限是随便加的,这里已经没有意义了
		UserDetails userDetails = User.withUsername(bean.getUserName()).password(bean.getPassword()).authorities("p1")
				.build();
		return userDetails;
	}
}

application.properties



		

spring.application.name=oauth-authorizationServer
server.port=8083

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

spring.main.allow-bean-definition-overriding=true

pom.xml

		
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.feizhou</groupId>
	<artifactId>oauth</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>oauth</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>

		<!--security    begin-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!--security    end-->

		<!--数据库依赖    begin-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.47</version>
		</dependency>
		<!--数据库依赖    end-->


		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-jwt</artifactId>
			<version>1.0.10.RELEASE</version>
		</dependency>


		<dependency>
			<groupId>org.springframework.security.oauth.boot</groupId>
			<artifactId>spring-security-oauth2-autoconfigure</artifactId>
			<version>2.3.4.RELEASE</version>
		</dependency>




		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

2.3、测试

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会为您解答。在上一篇博客中,我们已经介绍了OAuth2.0的登录流程以及单点登录的实现方式。接下来,我将为您介绍OAuth2.0的登出流程。 在OAuth2.0的登出流程中,与登录流程类似,也需要涉及到三个角色:用户、客户端和认证服务器。下面是OAuth2.0的登出流程: 1. 用户在客户端应用程序中发起登出请求,客户端应用程序将请求转发给认证服务器。 2. 认证服务器接收到登出请求后,会将用户的会话标识从认证服务器的存储中删除,并向客户端应用程序发回响应,告诉客户端应用程序用户已经登出。 3. 客户端应用程序接收到响应后,会清除本地存储的用户会话信息,并将用户重定向到认证服务器的登出页面。 4. 当用户在认证服务器登出页面上点击确认登出后,认证服务器会将用户重定向到客户端应用程序的登出回调URL,并在URL中包含一个参数,示用户已经登出。 5. 客户端应用程序接收到认证服务器的登出回调请求后,会清除本地存储的用户会话信息,并重定向用户到应用程序的登录页面。 需要注意的是,OAuth2.0的登出流程中并没有提供单点登出的实现方式,因为OAuth2.0本身并不支持单点登出。如果需要实现单点登出,可以借助于其他的技术手段,比如使用Redis等缓存技术来实现单点登出。 好的,以上就是OAuth2.0的登出流程。希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值