中英文切换的网站怎么做,手机网址怎么输入,担路网络科技有限公司的证书,佛山网站建设3lue3lue简介
上一节Springsecurity 用户认证 Springsecurity 拥有强大的认证和授权功能并且非常灵活#xff0c;,一来说我们都i有以下需求 可以帮助应用程序实现以下两种常见的授权需求#xff1a; 用户-权限-资源#xff1a;例如张三的权限是添加用户、查看用户列表#xff0c;李…简介
上一节Springsecurity 用户认证 Springsecurity 拥有强大的认证和授权功能并且非常灵活,一来说我们都i有以下需求 可以帮助应用程序实现以下两种常见的授权需求 用户-权限-资源例如张三的权限是添加用户、查看用户列表李四的权限是查看用户列表 用户-角色-权限-资源例如 张三是角色是管理员、李四的角色是普通用户管理员能做所有操作普通用户只能查看信息
RBAC权限模型
RBAC权限模型Role-Based Access Control即基于角色的权限控制。这是目前最常被开发者使用也是相对易用、通用权限模型 即每个用户对应不同角色,每个角色对应不同功能
用户-角色-权限-资源
RBACRole-Based Access Control基于角色的访问控制是一种常用的数据库设计方案它将用户的权限分配和管理与角色相关联。以下是一个基本的RBAC数据库设计方案的示例
用户表User table包含用户的基本信息例如用户名、密码和其他身份验证信息。
列名数据类型描述user_idint用户IDusernamevarchar用户名passwordvarchar密码emailvarchar电子邮件地址………
角色表Role table存储所有可能的角色及其描述。
列名数据类型描述role_idint角色IDrole_namevarchar角色名称descriptionvarchar角色描述………
权限表Permission table定义系统中所有可能的权限。
列名数据类型描述permission_idint权限IDpermission_namevarchar权限名称descriptionvarchar权限描述………
用户角色关联表User-Role table将用户与角色关联起来。
列名数据类型描述user_role_idint用户角色关联IDuser_idint用户IDrole_idint角色ID………
角色权限关联表Role-Permission table将角色与权限关联起来。
列名数据类型描述role_permission_idint角色权限关联IDrole_idint角色IDpermission_idint权限ID………
在这个设计方案中用户可以被分配一个或多个角色而每个角色又可以具有一个或多个权限。通过对用户角色关联和角色权限关联表进行操作可以实现灵活的权限管理和访问控制。
当用户尝试访问系统资源时系统可以根据用户的角色和权限决定是否允许访问。这样的设计方案使得权限管理更加简单和可维护因为只需调整角色和权限的分配即可而不需要针对每个用户进行单独的设置。其实就是权限或者角色不会直接在用户属性里,而是在多表联查中赋予 比如 来获取用户权限或者角色
select idselectPermsByUserId resultTypejava.lang.StringSELECTDISTINCT m.permsFROMsys_user_role urLEFT JOIN sys_role r ON ur.role_id r.idLEFT JOIN sys_role_menu rm ON ur.role_id rm.role_idLEFT JOIN sys_menu m ON m.id rm.menu_idWHEREuser_id #{userid}AND r.status 0AND m.status 0
/select基于request的授权
在首页的用户笔记中有项目目录,现在修改其中的俩个接口权限
官方架构
之前只要携带正确的token 就可以访问 修改配置文件后 Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(AbstractHttpConfigurer::disable).addFilterBefore(new JwtAuthenticationTokenFilter(redisCache,jwtUtil), UsernamePasswordAuthenticationFilter.class).sessionManagement(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth - auth.requestMatchers(HttpMethod.POST,/auth/login).permitAll() // 对登录接口允许匿名访问.requestMatchers(/user/list).hasAuthority(user:list).requestMatchers(/user/add).hasAuthority(user:add).requestMatchers(HttpMethod.OPTIONS).permitAll()
// .requestMatchers(**).permitAll().anyRequest().authenticated()).exceptionHandling(exception - exception.authenticationEntryPoint(new MyAuthenticationEntryPoint())).headers(headers - headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));return http.build();}返回的内容是自己定义的json 但是响应码确实变成了403 security不允许访问
实现授权:权限-资源
实现不同权限访问不同接口
配置文件限定接口
EnableMethodSecurity
Configuration
AllArgsConstructor
public class WebSecurityConfig {private final ApplicationEventPublisher applicationEventPublisher;Autowiredprivate RedisCache redisCache;Autowiredprivate JwtUtil jwtUtil;
// 从配置文件注入 ioc先扫描配置文件
ResourceUserMapper userMapper;/*** 这个Bean创建了一个认证管理器对象它是Spring Security认证的核心组件之一。* 认证管理器负责协调和管理认证流程并委托给一个或多个认证提供者在这里使用了daoAuthenticationProvider来进行具体的认证操作。* 这里通过创建一个ProviderManager对象将之前配置的daoAuthenticationProvider添加到认证管理器中。* 还通过setAuthenticationEventPublisher()方法设置了一个事件发布器用于在认证事件发生时发布相关的事件* 这里使用了DefaultAuthenticationEventPublisher并传入了一个applicationEventPublisher对象可能用于发布认证事件到Spring的事件机制中。* return*/Beanpublic AuthenticationManager authenticationManager() {ListAuthenticationProvider providerList new ArrayList();providerList.add(daoAuthenticationProvider());ProviderManager providerManager new ProviderManager(providerList);providerManager.setAuthenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationEventPublisher));return providerManager;}/*** 是Spring Security用于处理基于数据库的用户认证的提供者。* DaoAuthenticationProvider需要一个UserDetailsService对象来获取用户的详细信息进行认证* 所以通过setUserDetailsService()方法设置了我们之前设置的manager。* return*/BeanDaoAuthenticationProvider daoAuthenticationProvider() {DaoAuthenticationProvider daoAuthenticationProvider new DaoAuthenticationProvider();daoAuthenticationProvider.setPasswordEncoder( passwordEncoder());daoAuthenticationProvider.setUserDetailsService(new DBUserDetailsManager(userMapper));return daoAuthenticationProvider;}/*** 把默认的密码加密器换成我们自定义的加密器* return*/Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(AbstractHttpConfigurer::disable).addFilterBefore(new JwtAuthenticationTokenFilter(redisCache,jwtUtil), UsernamePasswordAuthenticationFilter.class).sessionManagement(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth - auth.requestMatchers(HttpMethod.POST,/auth/login).permitAll() // 对登录接口允许匿名访问.requestMatchers(/user/list).hasAuthority(user:list)//对接口定制限定权限.requestMatchers(/user/add).hasAuthority(user:add).requestMatchers(HttpMethod.OPTIONS).permitAll()
// .requestMatchers(**).permitAll()//对所有请求开启授权保护.anyRequest()//已认证的请求会被自动授权.authenticated()).exceptionHandling(// 异常结果处理 1.认证异常处理2.授权异常处理exception - exception.authenticationEntryPoint(new MyAuthenticationEntryPoint()).accessDeniedHandler((request, response, e)-{//创建结果对象HashMap result new HashMap();result.put(code, -1);result.put(message, 没有权限);//转换成json字符串String json JSON.toJSONString(result);//返回响应response.setContentType(application/json;charsetUTF-8);response.getWriter().println(json);})).headers(headers - headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));return http.build();}}规范用户类
改造用户类 这里做简单模拟 写一个字段作为权限字段,往往实际开发中是一个json 数组用于转换
/*** TableName user*/
TableName(value user)
Data
public class User implements Serializable {private Integer id;private String username;private String password;private Integer enabled;//实际开发中权限字段多半是json 里卖装的权限数组TableField(exist false)private ListGrantedAuthority authorities;private static final long serialVersionUID 1L;}security定义的规范用户类
Datapublic class UserDetail implements UserDetails {private User user;//存储SpringSecurity所需要的权限信息的集合 构造函数时候用于将user的json数组转换为这个权限对象private ListGrantedAuthority authorities;Overridepublic List? extends GrantedAuthority getAuthorities() {return authorities;}Overridepublic String getPassword() {return user.getPassword();}Overridepublic String getUsername() {return user.getUsername();}Overridepublic boolean isAccountNonExpired() {return true;}Overridepublic boolean isAccountNonLocked() {return true;}Overridepublic boolean isCredentialsNonExpired() {return true;}Overridepublic boolean isEnabled() {return true;}
}userdetailsService的实现类
Component
Slf4j
AllArgsConstructor
public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {private UserMapper userMapper;
// 这样就可以按照security的规范来使用用户的管理Overridepublic UserDetails updatePassword(UserDetails user, String newPassword) {return null;}Overridepublic void createUser(UserDetails userDetails) {
// 在sql中插入信息User user new User();user.setUsername(userDetails.getUsername());user.setPassword(userDetails.getPassword());user.setEnabled(1);userMapper.insert(user);}Overridepublic void updateUser(UserDetails user) {}Overridepublic void deleteUser(String username) {}Overridepublic void changePassword(String oldPassword, String newPassword) {}Overridepublic boolean userExists(String username) {return false;}//security底层会根据这个方法来对比用户Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 这里用户账户是唯一的User user userMapper.selectOne(Wrappers.UserlambdaQuery().eq(User::getUsername, username));if (user null){throw new UsernameNotFoundException(系统用户不存在);}else{
// 1表示可用boolean isenabled user.getEnabled() 1;
/*** 任何非零的整数值都会被视为 true而 0 会被视为 false。*/log.info(数据库个根据用户名获取用户user);//模拟系统权限列表ListGrantedAuthority authorities new ArrayList();// 写一个静态数据模拟用户数据库中的权限authorities.add(()-user:list);user.setAuthorities(authorities);UserDetail detail new UserDetail();detail.setUser(user);detail.setAuthorities(authorities);return detail;}}
}改造过滤器
Slf4j
AllArgsConstructorpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter {private final RedisCache redisCache;private final JwtUtil jwtUtil;Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {try {String token request.getHeader(token);if (tokenNotRequired(request.getRequestURI())) {
// 登录接口直接放行filterChain.doFilter(request, response);return;}if (token null || token.trim().isEmpty()) {throw new AuthenticationException(需要登录) {};}String username jwtUtil.getUsernameFromToken(token);String redisKey logintoken: username;String jsonString redisCache.getCacheObject(redisKey);if (jsonString null || jsonString.trim().isEmpty()) {throw new AuthenticationException(用户登录已过期) {};}UserDetail userInfo JSON.parseObject(jsonString, UserDetail.class);UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(userInfo, null, userInfo.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authenticationToken); //设置给上下文对象filterChain.doFilter(request, response);}//对所有抛出的异常进行处理catch (AuthenticationException e) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.setContentType(application/json; charsetutf-8);response.getWriter().println(JSON.toJSONString(Result.nologin(e.getMessage())));}}private boolean tokenNotRequired(String requestURI) {return /auth/login.equals(requestURI) || /auth/info.equals(requestURI);}
}访问上下文对象 GetMapping(/)public Result index() {SecurityContext context SecurityContextHolder.getContext();//存储认证对象的上下文Authentication authentication context.getAuthentication();//认证对象String username authentication.getName();//用户名Object principal authentication.getPrincipal();//身份Object credentials authentication.getCredentials();//凭证(脱敏)Collection? extends GrantedAuthority authorities authentication.getAuthorities();//权限System.out.println(username);System.out.println(principal);System.out.println(credentials);System.out.println(authorities);HashMapString, Object map new HashMap();map.put(认证对象, authentication);map.put(身份信息, principal);map.put(creden, credentials);return Result.success(map);}此时可以发现权限已经设置好了
上述的文件操作中,我在loaduserbyusername中只添加了user:list权限,现在再次访问这俩个有权限设置的接口
userl:list 权限
user:add权限 因为没有该权限 所以触发未授权处理器
实现用户-角色-资源
修改配置文件接口 http.csrf(AbstractHttpConfigurer::disable).addFilterBefore(new JwtAuthenticationTokenFilter(redisCache,jwtUtil), UsernamePasswordAuthenticationFilter.class).sessionManagement(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth - auth.requestMatchers(HttpMethod.POST,/auth/login).permitAll() // 对登录接口允许匿名访问.requestMatchers(/user/list).hasAuthority(user:list).requestMatchers(/user/add).hasRole(admin).requestMatchers(HttpMethod.OPTIONS).permitAll()
// .requestMatchers(**).permitAll().anyRequest().authenticated()).exceptionHandling(exception - exception.authenticationEntryPoint(new MyAuthenticationEntryPoint())).headers(headers - headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));return http.build();该接口
// 动态添加系统授权用户PostMapping(/add)public String addUser(RequestBody User user) {
log.info(add useruser);userService.adduser(user);
return 添加用户;}此时一个接口需要user:list权限 (查看用户列表) 一个需要当前用户是管理员角色 才能对系统用户进行新增
修改我们定义的用户
TableName(value user)
Data
public class User implements Serializable {private Integer id;private String username;private String password;private Integer enabled;//实际开发中权限字段多半是json 里卖装的权限数组TableField(exist false)private ListGrantedAuthority authorities;//模拟数据存放的都是用户json数组TableField(exist false)public ListString roles;private static final long serialVersionUID 1L;}修改userdetailsService实现类 静态赋予角色(模拟该用户数据中的角色 这里的用户表只有简单的用户密码和账户字段)
Component
Slf4j
AllArgsConstructor
public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {private UserMapper userMapper;// 这样就可以按照security的规范来使用用户的管理Overridepublic UserDetails updatePassword(UserDetails user, String newPassword) {return null;}Overridepublic void createUser(UserDetails userDetails) {
// 在sql中插入信息User user new User();user.setUsername(userDetails.getUsername());user.setPassword(userDetails.getPassword());user.setEnabled(1);userMapper.insert(user);}Overridepublic void updateUser(UserDetails user) {}Overridepublic void deleteUser(String username) {}Overridepublic void changePassword(String oldPassword, String newPassword) {}Overridepublic boolean userExists(String username) {return false;}//security底层会根据这个方法来对比用户Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 这里用户账户是唯一的User user userMapper.selectOne(Wrappers.UserlambdaQuery().eq(User::getUsername, username));if (user null){throw new UsernameNotFoundException(系统用户不存在);}else{
// 1表示可用boolean isenabled user.getEnabled() 1;
/*** 任何非零的整数值都会被视为 true而 0 会被视为 false。*/log.info(数据库个根据用户名获取用户user);ListString roles new ArrayList();//模拟数据中的该用户的角色数据//假如该用户有俩个数据roles.add(admin);roles.add(leader);//模拟系统权限列表ListGrantedAuthority authorities new ArrayList();// 写一个静态数据模拟用户数据库中的权限authorities.add(()-user:list);roles.forEach(role- authorities.add(()-ROLE_role));user.setAuthorities(authorities);UserDetail detail new UserDetail(user);detail.setAuthorities(authorities);return detail;}}
}关键代码块解析 这里创建一个集合模拟该用户在数据的角色数据,然后和用户数据的添加设置到userdetals的实现类中,这里为什么这么设置角色?为什么角色和权限放在同一字段
istString roles new ArrayList();//模拟数据中的该用户的角色数据//假如该用户有俩个数据roles.add(admin);roles.add(leader);//模拟系统权限列表ListGrantedAuthority authorities new ArrayList();// 写一个静态数据模拟用户数据库中的权限authorities.add(()-user:list);roles.forEach(role- authorities.add(()-ROLE_role));user.setAuthorities(authorities);打开限制接口的源码hasRole(“admin”) 发现和权限的区别只有多了一个前缀作为识别而已 重启项目访问上下文对象 角色本质底层也是权限,只是有一个前缀醉作为识别
访问限制接口
成功
已经和认证处理器一样拥有丰富的授权事件处理器 基于方法的授权
这个是偏向主流的鉴权官网地址-方法安全
配置文件类中添加如下注解 开启方法鉴权 这种开启功能的注解一般放在启动类
EnableMethodSecurityuserdetailService接口的实现类逻辑依旧是从数据库获取用户和权限等,所以不做修改依旧使用上面的实现类模拟-userdetailservice实现
将配置文件中有关授权的配置删除
Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(AbstractHttpConfigurer::disable).addFilterBefore(new JwtAuthenticationTokenFilter(redisCache,jwtUtil), UsernamePasswordAuthenticationFilter.class).sessionManagement(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth - auth.requestMatchers(HttpMethod.POST,/auth/login).permitAll() // 对登录接口允许匿名访问.requestMatchers(HttpMethod.OPTIONS).permitAll()
// .requestMatchers(**).permitAll().anyRequest().authenticated()).exceptionHandling(exception - exception.authenticationEntryPoint(new MyAuthenticationEntryPoint())).headers(headers - headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));return http.build();}对于需要限定的接口添加PreAuthorize注解
// 限定角色: 用户必须有 ADMIN 角色 并且 用户名是 admin 才能访问此方法PreAuthorize(hasRole(admin) and authentication.name admim)GetMapping(/list)
public ListUser getList(){return userService.list();
}//限定权限: 用户必须有 USER_ADD 权限 才能访问此方法PreAuthorize(hasAuthority(user:add))
PostMapping(/add)
public void add(RequestBody User user){userService.saveUserDetails(user);
}由于当前用户名是123 所以也会触发授权失败的处理 修改回来即可访问成功
outh2
springsecurity官网配置 OAuth 2.0 是一种开放标准的授权框架允许用户授权第三方应用访问他们存储在资源所有者比如一个网站或服务上的受保护资源而不需要将身份凭证比如用户名和密码直接暴露给第三方应用。OAuth 2.0 的主要设计目标是为了简化客户端开发并提供更强大的授权机制。OAuth 2.0 不是认证协议而是一个授权协议。它允许资源所有者在不泄露他们的凭据的情况下委托限定的访问权限。 OAuth 2.0 提供了一种安全的方式让用户允许第三方应用访问他们在其他网站或服务上的受保护资源而无需共享他们的用户名和密码。
1、OAuth2简介
1.1、OAuth2是什么
“Auth” 表示 “授权” Authorization
“O” 是 Open 的简称表示 “开放”
连在一起就表示 “开放授权”OAuth2是一种开放授权协议。
OAuth2最简向导The Simplest Guide To OAuth 2.0
1.2、OAuth2的角色
OAuth 2协议包含以下角色
资源所有者Resource Owner即用户资源的拥有人想要通过客户应用访问资源服务器上的资源。客户应用Client通常是一个Web或者无线应用它需要访问用户的受保护资源。资源服务器Resource Server存储受保护资源的服务器或定义了可以访问到资源的API接收并验证客户端的访问令牌以决定是否授权访问资源。授权服务器Authorization Server负责验证资源所有者的身份并向客户端颁发访问令牌。 1.3、OAuth2的使用场景
开放系统间授权
社交登录
在传统的身份验证中用户需要提供用户名和密码还有很多网站登录时允许使用第三方网站的身份这称为第三方登录。所谓第三方登录实质就是 OAuth 授权。用户想要登录 A 网站A 网站让用户提供第三方网站的数据证明自己的身份。获取第三方网站的身份数据就需要 OAuth 授权。 开放API
例如云冲印服务的实现 现代微服务安全
单块应用安全 微服务安全 企业内部应用认证授权 SSOSingle Sign On 单点登录 IAMIdentity and Access Management 身份识别与访问管理
1.4、OAuth2的四种授权模式
RFC6749
RFC 6749 - The OAuth 2.0 Authorization Framework (ietf.org)
阮一峰
OAuth 2.0 的四种方式 - 阮一峰的网络日志 (ruanyifeng.com)
四种模式
授权码authorization-code隐藏式implicit密码式password客户端凭证client credentials
第一种方式授权码
授权码authorization code指的是第三方应用先申请一个授权码然后再用该码获取令牌。
这种方式是最常用最复杂也是最安全的它适用于那些有后端的 Web 应用。授权码通过前端传送令牌则是储存在后端而且所有与资源服务器的通信都在后端完成。这样的前后端分离可以避免令牌泄漏。比如常见的jwt 注册客户应用客户应用如果想要访问资源服务器需要有凭证需要在授权服务器上注册客户应用。注册后会获取到一个ClientID和ClientSecrets 第二种方式隐藏式
隐藏式implicit也叫简化模式有些 Web 应用是纯前端应用没有后端。这时就不能用上面的方式了必须将令牌储存在前端。
RFC 6749 规定了这种方式允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤所以称为隐藏式。这种方式把令牌直接传给前端是很不安全的。因此只能用于一些安全要求不高的场景并且令牌的有效期必须非常短通常就是会话期间session有效浏览器关掉令牌就失效了。
https://a.com/callback#tokenACCESS_TOKEN
将访问令牌包含在URL锚点中的好处锚点在HTTP请求中不会发送到服务器减少了泄漏令牌的风险。第三种方式密码式
密码式Resource Owner Password Credentials如果你高度信任某个应用RFC 6749 也允许用户把用户名和密码直接告诉该应用。该应用就使用你的密码申请令牌。 比如一个没有和其他服务对接的应用
这种方式需要用户给出自己的用户名/密码显然风险很大因此只适用于其他授权方式都无法采用的情况而且必须是用户高度信任的应用。 第四种方式凭证式
凭证式client credentials也叫客户端模式适用于没有前端的命令行应用即在命令行下请求令牌。
这种方式给出的令牌是针对第三方应用的而不是针对用户的即有可能多个用户共享同一个令牌。
1.5、授权类型的选择 2、Spring中的OAuth2
2.1、相关角色
回顾 OAuth 2中的角色
资源所有者Resource Owner客户应用Client资源服务器Resource Server授权服务器Authorization Server
2.2、Spring中的实现
OAuth2 :: Spring Security
Spring Security
客户应用OAuth2 ClientOAuth2客户端功能中包含OAuth2 Login资源服务器OAuth2 Resource Server
Spring
授权服务器Spring Authorization Server它是在Spring Security之上的一个单独的项目。
2.3、相关依赖
!-- 资源服务器 --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-oauth2-resource-server/artifactId
/dependency!-- 客户应用 --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-oauth2-client/artifactId
/dependency!-- 授权服务器 --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-oauth2-authorization-server/artifactId
/dependency2.4、授权登录的实现思路
使用OAuth2 Login 比如登录淘宝可以使用微信登陆和支付宝登录 3、GiuHub社交登录案例
3.1、创建应用
注册客户应用
登录GitHub在开发者设置中找到OAuth Apps创建一个application为客户应用创建访问GitHub的凭据 填写应用信息默认的重定向URI模板为{baseUrl}/login/oauth2/code/{registrationId}。registrationId是ClientRegistration的唯一标识符。 获取应用程序id生成应用程序密钥 3.2、创建测试项目
创建一个springboot项目oauth2-login-demo创建时引入如下依赖 示例代码参考spring-security-samples/servlet/spring-boot/java/oauth2/login at 6.2.x · spring-projects/spring-security-samples (github.com)
3.3、配置OAuth客户端属性
application.yml
spring:security:oauth2:client:registration:github:client-id: 7807cc3bb1534abce9f2client-secret: 008dc141879134433f4db7f62b693c4a5361771b
# redirectUri: http://localhost:8200/login/oauth2/code/github3.4、创建Controller
package com.atguigu.oauthdemo.controller;Controller
public class IndexController {GetMapping(/)public String index(Model model,RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient,AuthenticationPrincipal OAuth2User oauth2User) {model.addAttribute(userName, oauth2User.getName());model.addAttribute(clientName, authorizedClient.getClientRegistration().getClientName());model.addAttribute(userAttributes, oauth2User.getAttributes());return index;}
}3.5、创建html页面
resources/templates/index.html
!DOCTYPE html
html xmlnshttp://www.w3.org/1999/xhtml xmlns:thhttps://www.thymeleaf.org xmlns:sechttps://www.thymeleaf.org/thymeleaf-extras-springsecurity5
headtitleSpring Security - OAuth 2.0 Login/titlemeta charsetutf-8 /
/head
body
div stylefloat: right th:fragmentlogout sec:authorizeisAuthenticated()div stylefloat:leftspan stylefont-weight:boldUser: /spanspan sec:authenticationname/span/divdiv stylefloat:nonenbsp;/divdiv stylefloat:rightform action# th:action{/logout} methodpostinput typesubmit valueLogout //form/div
/div
h1OAuth 2.0 Login with Spring Security/h1
divYou are successfully logged in span stylefont-weight:bold th:text${userName}/spanvia the OAuth 2.0 Client span stylefont-weight:bold th:text${clientName}/span
/div
divnbsp;/div
divspan stylefont-weight:boldUser Attributes:/spanulli th:eachuserAttribute : ${userAttributes}span stylefont-weight:bold th:text${userAttribute.key}/span: span th:text${userAttribute.value}/span/li/ul
/div
/body
/html3.6、启动应用程序
启动程序并访问localhost:8080。浏览器将被重定向到默认的自动生成的登录页面该页面显示了一个用于GitHub登录的链接。点击GitHub链接浏览器将被重定向到GitHub进行身份验证。使用GitHub账户凭据进行身份验证后用户会看到授权页面询问用户是否允许或拒绝客户应用访问GitHub上的用户数据。点击允许以授权OAuth客户端访问用户的基本个人资料信息。此时OAuth客户端访问GitHub的获取用户信息的接口获取基本个人资料信息并建立一个已认证的会话。
4、案例分析
4.1、登录流程
A 网站让用户跳转到 GitHub并携带参数ClientID 以及 Redirection URI。GitHub 要求用户登录然后询问用户A 网站要求获取用户信息的权限你是否同意用户同意GitHub 就会重定向回 A 网站同时发回一个授权码。A 网站使用授权码向 GitHub 请求令牌。GitHub 返回令牌.A 网站使用令牌向 GitHub 请求用户数据。GitHub返回用户数据A 网站使用 GitHub用户数据登录 4.2、CommonOAuth2Provider
CommonOAuth2Provider是一个预定义的通用OAuth2Provider为一些知名资源服务API提供商如Google、GitHub、Facebook预定义了一组默认的属性。
例如授权URI、令牌URI和用户信息URI通常不经常变化。因此提供默认值以减少所需的配置。
因此当我们配置GitHub客户端时只需要提供client-id和client-secret属性。
GITHUB {public ClientRegistration.Builder getBuilder(String registrationId) {ClientRegistration.Builder builder this.getBuilder(registrationId, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, //授权回调地址(GitHub向客户应用发送回调请求并携带授权码) {baseUrl}/{action}/oauth2/code/{registrationId});builder.scope(new String[]{read:user});//授权页面builder.authorizationUri(https://github.com/login/oauth/authorize);//客户应用使用授权码向 GitHub 请求令牌builder.tokenUri(https://github.com/login/oauth/access_token);//客户应用使用令牌向GitHub请求用户数据builder.userInfoUri(https://api.github.com/user);//username属性显示GitHub中获取的哪个属性的信息builder.userNameAttributeName(id);//登录页面超链接的文本builder.clientName(GitHub);return builder;}
},官方给的是整合github登录的列子 使用性不强,但是其他平台也是使用outh2 api作为俩调登录 比如我这里登录获取token 返回令牌就是隐藏式的