Spring Security OAuth2 JWT SSO 整合项目
前言
本项目为Spring Security OAuth2 JWT SSO整合项目。适合对Spring Security、OAuth2和JWT SSO有一定认识并且想要进行有机整合的各位,项目本着上手最简单,基础功能最完善的原则编写。 基于数据库的认证和鉴权。采用OAuth2认证,根据认证服务器端查询的用户信息,进行认证处理,根据权限在资源服务器进行鉴权。此处采用权限鉴权模式,根据用户的权限,开闸可以访问的资源服务器范围。【另外还有根据角色鉴权,只是换汤不换药】
for (int i = 0; i <= 2; i++) { System.out.println(“直接下载源码食用更佳!源码在文章最下方给出!”); } 源码已经进行详细的注解,可以结合文章一起理解。
举个栗子稍微理解一下Oauth2认证,网站使用微信认证的过程:(从上到下按按照箭头方向进行) 
一、使用步骤
1.工程结构

2.添加依赖
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.2.1.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.xxxx</groupId>
<artifactId>springsecurityoauth2-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springsecurityoauth2-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok
</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.数据库建表语句
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `um_t_role`;
CREATE TABLE `um_t_role` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`created_time` bigint(0) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`role` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `um_t_role` VALUES (1, '管理员拥有所有接口操作权限', 1627199362, '管理员', 'ADMIN');
INSERT INTO `um_t_role` VALUES (2, '普通拥有查看用户列表与修改密码权限,不具备对用户增删改权限', 1627199362, '普通用户', 'USER');
DROP TABLE IF EXISTS `um_t_role_user`;
CREATE TABLE `um_t_role_user` (
`role_id` int(0) NULL DEFAULT NULL,
`user_id` int(0) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `um_t_role_user` VALUES (1, 1);
DROP TABLE IF EXISTS `um_t_user`;
CREATE TABLE `um_t_user` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`account` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `um_t_user` VALUES (1, 'admin', '系统默认管理员', '$2a$10$N97RyMYeQ7aVTxLvdxq5NeBivdbj/u2GQtHERISUt8qhKBfnjSC1q', 'admin');
INSERT INTO `um_t_user` VALUES (2, 'user', '普通用户', '$2a$10$N97RyMYeQ7aVTxLvdxq5NeBivdbj/u2GQtHERISUt8qhKBfnjSC1q', 'user');
INSERT INTO `um_t_user` VALUES (3, 'user', 'test user', '$2a$10$N97RyMYeQ7aVTxLvdxq5NeBivdbj/u2GQtHERISUt8qhKBfnjSC1q', 'Jacks');
SET FOREIGN_KEY_CHECKS = 1;
所有用户密码都是123456    
4.编写相关配置文件
(1)generatorConfig.xml 是mybatis-generator的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<classPathEntry
location="D:\maven-repo\mysql\mysql-connector-java\8.0.18\mysql-connector-java-8.0.18.jar"/>
<context id="MysqlTables" targetRuntime="MyBatis3">
<property name="autoDelimitKeywords" value="true"/>
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/auth_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"
userId="root"
password="123456">
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<javaModelGenerator targetPackage="com.xxxx.springsecurityoauth2demo.model.pojo"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
<property name="immutable" value="false"/>
</javaModelGenerator>
<sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.xxxx.springsecurityoauth2demo.model.dao"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<table schema="root" tableName="um_t_role" domainObjectName="Role"
enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
<table schema="root" tableName="um_t_user" domainObjectName="User" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
(2)application.properties
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/auth_test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
mybatis.mapper-locations=classpath:mappers/*.xml
5.利用mybatis-generator生成实体类、Mapper、XML文档。

6.Security、OAuth2、JWT、SSO配置类
(1)授权服务器
用来进行授权配置。需要继承AuthorizationServerConfigurerAdapter类,重写configure()方法.
AuthorizationServerConfig.java
package com.xxxx.springsecurityoauth2demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
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.provider.token.TokenEnhancer;
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;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private AuthenticationManager authenticationManager;
@Resource
private UserDetailsService userDetailsService;
@Resource(name = "jwtTokenStore")
private TokenStore tokenStore;
@Resource(name = "jwtAccessTokenConverter")
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Resource
private JwtTokenEnhancer jwtTokenEnhancer;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain chain = new TokenEnhancerChain();
List<TokenEnhancer> delegates = new ArrayList<>();
delegates.add(jwtTokenEnhancer);
delegates.add(jwtAccessTokenConverter);
chain.setTokenEnhancers(delegates);
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenStore(tokenStore)
.accessTokenConverter(jwtAccessTokenConverter)
.tokenEnhancer(chain);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret(passwordEncoder.encode("112233"))
.redirectUris("http://www.baidu.com")
.accessTokenValiditySeconds(60 * 10)
.refreshTokenValiditySeconds(60 * 60 * 24)
.scopes("all")
.autoApprove(true)
.authorizedGrantTypes("authorization_code", "password", "refresh_token");
}
}
(2)资源管理器
企业生产环境下授权服务器和资源服务器是两个单独的服务器,我们为了学习,使用了单Model项目,所以放在了一起。
ResourceServerConfig.java
package com.xxxx.springsecurityoauth2demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**").and()
.authorizeRequests()
.antMatchers("/api/user/save").hasAuthority("admin")
.anyRequest()
.authenticated();
}
}
(3)JWT内容增强器
JwtTokenEnhancer.java
package com.xxxx.springsecurityoauth2demo.config;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.HashMap;
import java.util.Map;
public class JwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
Map<String, Object> info = new HashMap();
info.put("enhance", "增强的信息");
((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);
return oAuth2AccessToken;
}
}
(4)TokenStore配置类
JwtTokenStoreConfig.java
package com.xxxx.springsecurityoauth2demo.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;
@Configuration
public class JwtTokenStoreConfig {
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey("test_key");
return jwtAccessTokenConverter;
}
@Bean
public JwtTokenEnhancer jwtTokenEnhancer() {
return new JwtTokenEnhancer();
}
}
(5)Security核心配置类
SecurityConfig.java
package com.xxxx.springsecurityoauth2demo.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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().accessDeniedPage("/unauth.html");
http.authorizeRequests()
.antMatchers("/oauth/**", "/login/**", "/logout/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
7.配置Spring Security的UserDetailService实现从数据库查询信息进行认证
(1)MyUserService接口
package com.xxxx.springsecurityoauth2demo.service;
public interface MyUserService{
}
(2)MyUserServiceImpl
package com.xxxx.springsecurityoauth2demo.service.impl;
import com.xxxx.springsecurityoauth2demo.model.pojo.SecurityUser;
import com.xxxx.springsecurityoauth2demo.model.pojo.User;
import com.xxxx.springsecurityoauth2demo.service.MyUserService;
import com.xxxx.springsecurityoauth2demo.service.UserService;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class MyUserServiceImpl implements UserDetailsService, MyUserService {
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.getUserByUserName(username);
String name = user.getName();
String password = user.getPassword();
String authority = user.getAccount();
return new SecurityUser(name, password, AuthorityUtils.commaSeparatedStringToAuthorityList(authority));
}
}
(3)自定义Security框架的User实体
SecurityUser.java
package com.xxxx.springsecurityoauth2demo.model.pojo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
public class SecurityUser implements UserDetails {
private String username;
private String password;
private List<GrantedAuthority> authorities;
public SecurityUser(String username, String password, List<GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
8.Controller层
(1)针对jwt token测试用的Controller
TestUserController.java
package com.xxxx.springsecurityoauth2demo.controller;
import io.jsonwebtoken.Jwts;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
@RestController
@RequestMapping("/user")
public class TestUserController {
@RequestMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication, HttpServletRequest request) {
String header = request.getHeader("Authorization");
String token = header.substring(header.lastIndexOf("bearer") + 7);
return Jwts.parser()
.setSigningKey("test_key".getBytes(StandardCharsets.UTF_8))
.parseClaimsJws(token)
.getBody();
}
}
(2)User资源服务器 UserController
UserService.java
package com.xxxx.springsecurityoauth2demo.controller;
import com.xxxx.springsecurityoauth2demo.model.req.ReqUser;
import com.xxxx.springsecurityoauth2demo.service.UserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Resource
private UserService userService;
@PostMapping("/users")
public Object getAllUsers() {
return userService.getAllUsers();
}
@PostMapping("/save")
public Object save(@RequestBody ReqUser reqUser) {
return userService.save(reqUser);
}
}
9.Service层
(1)UserService接口
UserService.java
package com.xxxx.springsecurityoauth2demo.service;
import com.xxxx.springsecurityoauth2demo.model.pojo.User;
import com.xxxx.springsecurityoauth2demo.model.req.ReqUser;
public interface UserService {
Object getAllUsers();
User getUserByUserName(String username);
Object save(ReqUser reqUser);
}
UserServiceImpl.java
package com.xxxx.springsecurityoauth2demo.service.impl;
import com.xxxx.springsecurityoauth2demo.model.dao.UserMapper;
import com.xxxx.springsecurityoauth2demo.model.pojo.User;
import com.xxxx.springsecurityoauth2demo.model.req.ReqUser;
import com.xxxx.springsecurityoauth2demo.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public Object getAllUsers() {
List<User> users = userMapper.selectAllUsers();
return users;
}
@Override
public User getUserByUserName(String username) {
return userMapper.selectUserByUsername(username);
}
@Override
public Object save(ReqUser reqUser) {
int count = userMapper.save(reqUser);
return count;
}
}
10.Req对象
User请求参数对象ReqUser ReqUser.java
package com.xxxx.springsecurityoauth2demo.model.req;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ReqUser {
private String account;
private String description;
private String password;
private String name;
}
二、postman测试
(1)密码模式

用户直接输入密码,Client会拿用户名密码直接访问第三方,第三方认证后回执Access Token。多用于同一项目不同端口,比如维新运动需要微信认证,大家都是同一个项目,知道了密码也没什么关系。
需要访问固定接口/oauth/token 输入固定参数  
拿到token后,凭token可以访问到资源服务器的内容
(1)先访问TestController里的getCurrentUser来解析一下token。(也可以去JWT官网或其他网站进行解析,这里我是自己直接编写了代码解析) 
如果token不正确,则认证失败,禁止访问 
如果token过期,禁止访问

访问UserController.java接口与数据库进行交互 先访问一个只要认证通过就可以访问的不需要鉴权的接口 
再访问一个只允许admin权限访问的接口
成功添加用户。

换成权限为user的用户访问此接口,则经过鉴权之后拒绝授权访问。  
(2)授权码模式
需要访问指定地址先获取授权码,再根据授权码获取token  User-Agent相当于你用第三方登录弹出的登录网页)会带上 客户凭证(client Credentials)和重定向的uri去到要认证服务器里面去,认证服务器会让Resource Owner也就是用户去认证,用户同意了,会返回一个授权码给User-Agent再给Client。客户端Client拿到授权码之后会跟去我们重定向的URI和授权码再次去找到授权服务器,此时认证服务器会根据授权码返回一个Access Token(可以也返回Refresh Toke)。Access Token是必须要有的,Refresh Token是选择性的。
这里用普通权限user去访问需要admin权限的api/save接口 首先获取授权码,访问固定的url获取授权码
访问此链接:
http://localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
这个链接的含义http://localhost:8080的 /oauth/authorize 【固定接口地址】到我们的授权服务器请求,response_type=code去获取授权码 &client_id=client指定client的id 【自己定义的client的id】&redirect_uri【自己要重定向的】重定向地址 &scope=all就是全部范围。
输入上面的url会自动跳转登录页面,道理很简单,授权码模式不会携带用户名和密码,所以需要你登录,这也是授权码模式和密码模式的区别之一了。

code=授权码   接下来我们可有凭借token去访问资源了
(3)refresh_token模式
因为JWT Token是无状态的,不会在服务器端存储,所以我们需要设定Access Token的有效时间,而且时间不能太长,否则安全性会差。(如果你不设置Access Token的有效时间那么毫无安全性可言了)但是Access Token过期太快还要用户重新获取授权码,也就是重新输入用户名密码登录吗?那这样用户体验度也太差了吧?于是就有了refresh_token,当Access_Token过期时,只需要再检验refresh_token便可以重新获得Access_Token。所以一般而言,Access_Token的有效期会很短,而refresh_token的有效期会比较长。
先正常获取
 假设Access_token已经过期了,我们只需要凭借refresh_token便可以拿到新的Access_Token,不需要输入用户名密码等参数了。 
三、整合SSO
1.什么是SSO(Single Sign On)
单点登录全称Single Sign On (以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分

说人话就是当你在登录百度首页之后,左边红色框内的百度系统旗下加粗样式站点都不需要登录了,实现了一次性鉴别,全网站登录,对用户而言极为友好。
 相比于单系统登录, sso需要 个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息, 其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。
2.项目整合实现SSO
(1)客户端工程结构

application.properties
server.port=8081
server.servlet.session.cookie.name=OAUTH2-CLIENT-SESSIONID01
oauth2-server-url:http://localhost:8080
security.oauth2.client.client-id=client
security.oauth2.client.client-secret=112233
security.oauth2.client.user-authorization-uri=${oauth2-server-url}/oauth/authorize
security.oauth2.client.access-token-uri=${oauth2-server-url}/oauth/token
security.oauth2.resource.jwt.key-uri=${oauth2-server-url}/oauth/token_key
Controller
Controller.java (简单实现,测试用)
package com.xxxx.oauth2client01demo.controller;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class Controller {
@RequestMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication) {
return authentication;
}
}
项目入口开启SSO注解

(2)认证服务器端工程结构
我们为了学习和测试,沿用本项目的认证服务器,不再单独创建认证服务器。
指定重定向地址

单点登录配置

3.SSO测试

跳转到认证服务器完成认证之后,自动跳回要访问的接口地址,此时可以访问客户端的资源。 
四、 项目源码
项目源码地址 GitHub: https://github.com/pleineluna/luna Gitee:https://gitee.com/tsukuyo98/luna
SSO客户端源码地址: GitHub: https://github.com/pleineluna/oauth2-client01 Gitee:https://gitee.com/tsukuyo98/oauth2-client01
(顺便求各位点个Star?吧)
参考资料: https://www.cnblogs.com/xiaofengxzzf/p/10733955.html https://www.bilibili.com/video/BV1Cz4y1k7rd
?
|