单点登陆系统
一:服务设计
基于单点登陆系统中的业务描述,进行初步服务架构设计:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wW3eJ3fL-1645616815907)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/18f2af95bf6c46f4af8e3ae291cd9095-16413803696641.png)]
说明: 服务基于业务进行划分
- system(系统)服务: 通过连接数据库提供基础数据(例如用户信息,日志信息等)
- auth(认证服务): 负责完成用户身份的校验,密码的比对
- **resource(资源服务)😗*代表客户需要操作的资源(业务服务(例如我的订单,我的收藏等等).)
- gateway(网关服务):API网关是服务访问入口,身份认证,资源访问都通过网关进行资源统一转发,流控等
- UI(客户端):前后端分离架构设计,前端工程服务基于springboot web服务进行实现。
二:工程结构设计
基于服务的划分,设计工程结构如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-isXGuxEb-1645616815910)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/4f940e5644794a4abb7275a36993c346-16413808992443.png)]
三:SSO父工程创建
1.创建父工程
第一步:创建父工程:
2.父工程pom文件初始配置
第二步:初始化pom文件,添加依赖;
<!--maven父工程的pom文件中一般要定义子模块,
子工程中所需依赖版本的管理,公共依赖并且父工程的打包方式一般为pom方式-->
<dependencyManagement>
<dependencies>
<!--spring boot 核心依赖版本定义(spring官方定义)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Cloud 微服务规范(由spring官方定义)-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Cloud alibaba 依赖版本管理 (参考官方说明)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--第二步: 添加子工程的所需要的公共依赖-->
<dependencies>
<!--lombok 依赖,子工程中假如需要lombok,不需要再引入-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</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><!--项目构建配置,我们基于maven完成项目的编译,测试,打包等操作,
都是基于pom.xml完成这一列的操作,但是编译和打包的配置都是要写到build元素
内的,而具体的编译和打包配置,又需要plugin去实现,plugin元素不是必须的,maven
有默认的plugin配置,常用插件可去本地库进行查看-->
<plugins>
<!--通过maven-compiler-plugin插件设置项目
的统一的jdk编译和运行版本-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
四:system系统服务
1.业务描述
本次设计系统服务(System),主要用于提供基础数据服务,例如日志信息,用户信息等。
2.表结构设计
数据库中表的联系如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qNfiiOZU-1645616815911)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/image-20220105191513029-16413813143317.png)]
3.创建系统服务工程
第一步:创建sso-system工程(注意在父工程下面创建)
第二步:添加项目依赖
<dependencies>
<!--连接数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!--添加nacos注册和配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 添加配置依赖的话:需要将配置名改为:bootstrap.yml-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--添加web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<!--添加sentinel依赖,如果需要限流-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
</project>
第三步:在项目中添加bootstrap.yml文件
-
server: port: 8061 #端口号 spring: application: name: sso-system #服务名 cloud: nacos: discovery: #注册地址 server-addr: localhost:8848 config: #配置地址 server-addr: localhost:8848 file-extension: yml datasource: #可配置在配置中心 方便后期更改; url: jdbc:mysql:///jt-sso?serverTimezone=Asia/Shanghai&characterEncoding=utf8 username: root password: root
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E7JmZVrZ-1645616815912)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/image-20220105192253902-164138177525712.png)]
第四步:在项目中添加启动类
-
package com.jt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SystemApplication { public static void main(String[] args) { SpringApplication.run(SystemApplication.class,args); } }
第五步:在项目中添加单元测试类,测试数据库连接
-
@SpringBootTest public class SpringbootTest { /** * DataSource是一个数据源标准或者规范,java所有连接池需要基于这个规范进行实现; * 项目中添加了starter-jdbc依赖后,系统会自动帮我们引入一个HikariCP连接池 * 这个连接池有一个HikariDataSource对象就是基于上面的规范,这个对象在springboot启动时 * ,进行自动配置(DataSourceAutoConfiguration) */ @Autowired private DataSource dataSource; //HikariDataSource @Test void testGetConnection() throws SQLException { //通过DataSource获取连接时,首先要获取的是连接池HikariPool //然后从池中获取连接connectionBag //池是使用CopyOnWriteArrayList进行创建存储 //连接池:可以解决每次连接时三次握手和断开连接的四次挥手,大大提高效率--享元模式; //HikariPool--connectionBag //这里三个设计模式:双重校验单例模式(连接池),享元模式(共享池中的对象),桥接模式 Connection connection = dataSource.getConnection(); System.out.println(connection); } }
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZP0ozp2S-1645616815912)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/image-20211229184458375-164077469944713.png)]
4. Pojo对象逻辑实现
添加项目User对象,用于封装用户信息。
-
/** * 创建pojo对象 基于此对象存储用户信息 * 记住: * java中所有用于存储数据的对象,都建议实现序列化 * 并且添加一个序列化id. * 可以参考:string ,Integer,Arraylist,hashmap....... */ @Data @Accessors(chain = true) @TableName("tb_users") //假如sql语句自己写,则不需要通过此注解添加注解; public class User implements Serializable { //生成序列化id //如果id不写,反序列化时结构改变时会失败; private static final long serialVersionUID = 524959160790169643L; private Long id; private String username; private String password; private String status; }
5.Mapper对象逻辑实现
第一步:创建UserMapper接口,并定义基于用户名查询用户信息,基于用户id查询用户权限信息的方法
-
package com.jt.system.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.jt.system.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import java.util.List; @Mapper public interface UserMapper extends BaseMapper<User> { /** * 基于用户名获取用户信息 * @param username * @return */ @Select("select id,username,password,status " + "from tb_users " + "where username=#{username}") User selectUserByUsername(String username); /** * 基于用户id查询用户权限 * @param userId 用户id * @return 用户的权限 * 涉及到的表:tb_user_roles,tb_role_menus,tb_menus 三表联查; */ @Select("select distinct m.permission " + "from tb_user_roles ur join tb_role_menus rm on ur.role_id=rm.role_id" + " join tb_menus m on rm.menu_id=m.id " + "where ur.user_id=#{userId}") List<String> selectUserPermissions(Long userId); }
第二步:创建UserMapperTests类,对业务方法做单元测试
-
package com.jt; import com.jt.system.pojo.User; import com.jt.system.dao.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest public class UserMapperTests { @Autowired private UserMapper userMapper; @Test void testSelectUserByUsername(){ User user = userMapper.selectUserByUsername("admin"); System.out.println(user); } @Test void testSelectUserPermissions(){ List<String> permission= userMapper.selectUserPermissions(1L); System.out.println(permission); } }
6.Service对象逻辑实现
创建UserService接口及实现泪,定义用户及用户权限查询逻辑
第一步:定义service接口:
-
package com.jt.system.service; import com.jt.system.pojo.User; import java.util.List; public interface UserService { User selectUserByUsername(String username); List<String> selectUserPermissions(Long userId); }
第二步:定义service接口实现类
-
package com.jt.system.service.impl; import com.jt.system.dao.UserMapper; import com.jt.system.pojo.User; import com.jt.system.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User selectUserByUsername(String username) { return userMapper.selectUserByUsername(username); } @Override public List<String> selectUserPermissions(Long userId) { return userMapper.selectUserPermissions(userId); } }
7. Controller对象逻辑实现
-
package com.jt.system.controller; import com.jt.system.pojo.User; import com.jt.system.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/user/") public class UserController { @Autowired private UserService userService; @GetMapping("/login/{username}") public User doSelectUserByUsername( @PathVariable("username") String username){ return userService.selectUserByUsername(username); } @GetMapping("/permission/{userId}") public List<String> doSelectUserPermissions( @PathVariable("userId") Long userId){ return userService.selectUserPermissions(userId); } }
8. 启动服务进行访问测试
启动sso-system工程服务,打开浏览器分别对用户及用户权限信息的获取进行访问测试
-
1.基于用户名查询用户信息
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1Xr11F2-1645616815913)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/5bffa4e4dde8480bb33bd014d203d95e-164138264827813.png)]
-
2.基于用户id(这里假设用户id为1)查询用户权限
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yFExIbQW-1645616815913)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/4dde3f00d1d349f6bb2656fc9dc59396.png)]
五:auth(认证服务)
1.业务描述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bYIyMinn-1645616815913)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIyMTcyMTMz,size_16,color_FFFFFF,t_70.jpeg)]
用户登陆时调用此工程对用户身份进行统一身份认证和授权。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h0vqWER2-1645616815914)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/43666cf7cc3d41a3a125449a6016851b.png)]
2.创建工程
第一步:创建sso-auth工程
第二步:打开sso-auth工程中的pom文件,然后添加如下依赖:
-
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--SSO技术方案:SpringSecurity+JWT+oauth2--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <!--open feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
第三步:在sso-auth工程中创建bootstrap.yml文件
-
server: port: 8071 spring: application: name: sso-auth cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848
第四步 添加项目启动类
-
package com.jt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @EnableFeignClients //启动Feign 通过feign方式调用system服务获取数据; @SpringBootApplication public class AuthApplication { public static void main(String[] args) { SpringApplication.run(AuthApplication.class, args); } }
3.启动并访问项目
项目启动时,系统会默认生成一个登陆密码:
打开浏览器输入http://localhost:8071呈现登陆页面
4.定义user对象
第一步:定义User对象,用于封装从数据库查询到的用户信息
-
package com.jt.auth.pojo; import lombok.Data; import java.io.Serializable; @Data public class User implements Serializable { private static final long serialVersionUID = 4831304712151465443L; private Long id; private String username; private String password; private String status; }
第二步:定义远程Service对象,用于实现远程用户信息调用
-
package com.jt.auth.service; import com.jt.auth.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import java.util.List; //sso-system: 需要访问的服务 @FeignClient(value = "sso-system", contextId ="remoteUserService" ) public interface RemoteUserService { @GetMapping("/user/login/{username}") User selectUserByUsername( @PathVariable("username") String username); @GetMapping("/user/permission/{userId}") List<String> selectUserPermissions(@PathVariable("userId") Long userId); }
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7GgqD2O-1645616815914)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/image-20220105194940833-164138338212016.png)]
第三步:定义用户登陆业务逻辑处理对象
-
/**构建UserDetailsService接口实现类,在此类中基于 * RemoteUserService接口进行远程服务调用,调用sso-system * 获取用户信息; */ @Service @Slf4j public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserService remoteUserService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //1.基于用户名获取用户信息; com.jt.pojo.User user = remoteUserService.getUserByName(username); if (user == null) { throw new UsernameNotFoundException("用户不存在"); } //2.根据用户的id 获取用户的权限; List<String> permissions = remoteUserService.selectUserPermission(user.getId()); //输出日志 log.info("permissions{}"+permissions); //3.封装用户信息并返回:用户名/密码/权限 //使用spring下的user类中的API //import org.springframework.security.core.userdetails.User; User userDetails=new User(username, user.getPassword(),//这个密码是从数据库中获取的,与前端传入的进行对比验证; //将权限转换为list集合 AuthorityUtils.createAuthorityList(permissions.toArray(new String[] {}))); //4.将信息交给spring security的认证中心,进行认证分析; return userDetails; } }
5. 定义Security配置类
定义Spring Security配置类,在此类中配置认证规则
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rbJJ2et9-1645616815915)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/image-20220105195911397.png)]
-
/** * 当我们在执行登录操作时,底层逻辑(了解): * 1)Filter(过滤器) * 2)AuthenticationManager (认证管理器) * 3)AuthenticationProvider(认证服务处理器) * 4)UserDetailsService(负责用户信息的获取及封装) ok */
-
package com.jt.auto.config; import org.codehaus.jackson.map.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { //此对象对密码进行加密;此对象提供了一种不可逆的加密方式,相对于md5方式会更加安全 @Bean //多次调用,只创建一个对象; public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 定义认证管理器对象,这个对象负责完成用户信息的认证, * 即判定用户身份信息的合法性,在基于oauth2协议完成认 * 证时,需要此对象,所以这里讲此对象拿出来交给spring管理 * @return * @throws Exception */ @bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /**此方法为http请求配置方法,可以在此方法中配置; * 1.哪些资源放行(不登录可以访问),假如不做配置则默认所有资源都可以访问 * 2.哪些资源必须认证(登录)之后才可以访问 * @param * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { //super.configure(auth); //1.设置禁用跨域攻击:假如没有设置那么所有的第三方工具登录测试都会产生403无权限异常; //只是为了测试 后面完成可以取消; http.csrf().disable(); // authorizeRequests 相当于拦截器 //2.所有的资源都需要认证登录的设置; 匹配器 证实 http.authorizeRequests().antMatchers("/**").authenticated(); //3.设置所有资源都放行,无需认证登录; http.authorizeRequests().anyRequest().permitAll(); //4.配置需要认证的其他的都放行 //需求:对于default.html需要认证,其余资源无需认证; http.authorizeRequests(). antMatchers("/default.html").authenticated() .anyRequest().permitAll(); //5.登录配置(去哪里认证,认证成功或失败的处理器是谁) http.formLogin().defaultSuccessUrl("/index.html"); //6.前后端分离的一种做法是登录成功要返回json数据 //这句话会对外暴露一个登录路径/login http.formLogin(). //实现表达提交的认证 successHandler(successHandler()) .failureHandler(failureHandler()); } /**登录成功处理器*/ @Bean public AuthenticationSuccessHandler successHandler(){ return new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest Request, HttpServletResponse Response, Authentication authentication) throws IOException, ServletException { //构建map对象封装到要响应到客户端的数据 Map<String,Object> map=new HashMap<>(); map.put("status", "200"); map.put("message", "登录成功"); //调用此方法将map转为json格式; writeJsonToClient(Response, map); } }; } /**登陆失败处理器*/ @Bean public AuthenticationFailureHandler failureHandler() { return new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure( HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { //构建map对象封装到要响应到客户端的数据 Map<String, Object> map = new HashMap<>(); map.put("status", 500); map.put("message", "login failure"); writeJsonToClient(response, map); } }; } //自定义一个方法,进行格式转换; private void writeJsonToClient( HttpServletResponse response, Map<String,Object> map) throws IOException { //设置响应数据的编码方式 response.setCharacterEncoding("utf-8"); //设置响应数据的类型 response.setContentType("application/json;charset=utf-8"); //创建输出流对象,将数据转换成json格式输出到前端页面; PrintWriter writer=response.getWriter(); String jsonStr = new ObjectMapper().writeValueAsString(map); writer.println(jsonStr); writer.flush(); } }
基于Postman进行访问测试
启动sso-system,sso-auth服务,然后基于postman访问网关,执行登录测试:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AM2ns9m2-1645616815915)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/6d21271fbe3042dda0220524dbac4690.png)]
6.构建令牌生成及配置对象
-
借助JWT(Json Web Token-是一种json格式)方式将用户相关信息进行组织和加密,并作为响应令牌(Token),从服务端响应到客户端,客户端接收到这个JWT令牌之后,将其保存在客户端(例如localStorage),然后携带令牌访问资源服务器,资源服务器获取并解析令牌的合法性,基于解析结果判定是否允许用户访问资源.
-
package com.jt.auth.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 TokenConfig { /** * 配置令牌的存储策略,对于oauth2规范中提供了这样的几种策略 * 1)JdbcTokenStore(这里是要将token存储到关系型数据库) * 2)RedisTokenStore(这是要将token存储到redis数据库-key/value) * 3)JwtTokenStore(这里是将产生的token信息存储客户端,并且token * 中可以以自包含的形式存储一些用户信息) * 4).... */ @Bean public TokenStore tokenStore(){ //这里采用JWT方式生成和存储令牌信息 return new JwtTokenStore(jwtAccessTokenConverter()); } /** * 配置令牌的创建及验签方式 * 基于此对象创建的令牌信息会封装到OAuth2AccessToken类型的对象中 * 然后再存储到TokenStore对象,外界需要时,会从tokenStore进行获取。 */ @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter jwtAccessTokenConverter= new JwtAccessTokenConverter(); //JWT令牌构成:header(签名算法,令牌类型),payload(数据部分),Signing(签名) //这里的签名可以简单理解为加密,加密时会使用header中算法以及我们自己提供的密钥, //这里加密的目的是为了防止令牌被篡改。(这里密钥要保管好,要存储在服务端) jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);//设置密钥 return jwtAccessTokenConverter; } /** * JWT 令牌签名时使用的密钥(可以理解为盐值加密中的盐) * 1)生成的令牌需要这个密钥进行签名 * 2)获取的令牌需要使用这个密钥进行验签(校验令牌合法性,是否被篡改过) */ private static final String SIGNING_KEY="auth"; }
7.定义Oauth2认证授权配置
不是所有配置类都可以作为OAuth2.0认证中心的配置类,需要满足以下两点:
- 继承AuthorizationServerConfigurerAdapter
- 标注 @EnableAuthorizationServer 注解
AuthorizationServerConfigurerAdapter需要实现的三个方法如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4D3C5Whf-1645616815915)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/image-20220109130158718-164170452014867.png)]
1. 客户端配置
第一步:向认证中心申请几个参数:请求url:
-
/oauth/authorize?client_id=&response_type=code&scope=&redirect_uri=
-
client_id:客户端唯一id,认证中心颁发的唯一标识
-
client_secret:客户端的秘钥,相当于密码
-
scope:客户端的权限
-
redirect_uri:授权码模式使用的跳转uri,需要事先告知认证中心。
-
clients.inMemory() //客户端标识 (id) .withClient("gateway-client") //客户端密钥(随意) .secret(passwordEncoder.encode("123456")) //指定认证类型:密码模式(码密,刷新令牌,三方令牌,...) edGrantTypes("password","refresh_token") //作用域(在这里可以理解为只要包含我们规定信息的客户端都可以进行认证) .scopes("all"); }
-
-
authorizedGrantTypes()
:定义认证中心支持的授权类型,总共支持五种 -
- 授权码模式:authorization_code
- 密码模式:password
- 客户端模式:client_credentials
- 简化模式:implicit
- 令牌刷新:refresh_token,这并不是OAuth2的模式,定义这个表示认证中心支持令牌刷新
2. 令牌访问安全约束配置
主要对一些端点的权限进行配置,代码如下:
-
//super.configure(security); //对外发布认证入口(/oauth/token),认证通过服务端会生成一个令牌 security.tokenKeyAccess("permitAll()") //对外发布检查令牌的入口(/oauth/check_token) .checkTokenAccess("permitAll()") //允许用户通过表单方式提交认证,完成认证 .allowFormAuthenticationForClients(); }
3.令牌访问端点的配置
- 配置了密码模式所需要的AuthenticationManager
- 配置了令牌管理服务,AuthorizationServerTokenServices
- 配置
/oauth/token
申请令牌的uri只允许POST提交。
spring Security框架默认的访问端点有如下6个:
-
/oauth/authorize:获取授权码的端点
-
/oauth/token:获取令牌端点。
-
/oauth/confifirm_access:用户确认授权提交端点。
-
/oauth/error:授权服务错误信息端点。
-
/oauth/check_token:用于资源服务访问的令牌解析端点。
-
/oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话。
第二步:客户端拿到授权码之后,直接携带授权码发送请求给认证中心获取令牌,请求的url如下:
-
/oauth/token? client_id=& client_secret=& grant_type=authorization_code& code=NMoj5y& redirect_uri=
-
grant_type:授权类型,授权码固定的值为authorization_code
-
code:这个就是上一步获取的授权码
第三步: 返回令牌
认证中心收到令牌请求之后,通过之后,会返回一段JSON数据,其中包含了令牌access_token,如下:
-
{ "access_token":"ACCESS_TOKEN", "token_type":"bearer", "expires_in":2592000, "refresh_token":"REFRESH_TOKEN", "scope":"read", "uid":100101 }
-
access_token则是颁发的令牌,refresh_token是刷新令牌,一旦令牌失效则携带这个令牌进行刷新。
所有零件准备好了开始拼装最后的主体部分,这个主体部分就是授权服务器的核心配置.
-
package com.jt.auth.config; import lombok.AllArgsConstructor; 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.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancer; 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; import java.util.UUID; /** * Oauth2 是一种认证授权规范,它基于认证和授权定义了一套规则,在这套规则中规定了 * 实现一套认证授权系统需要哪些对象: * 1)系统资源(数据) * 2)资源拥有者(用户) * 3)管理资源的服务器 * 4)对用户进行认证和授权的服务器 * 5)客户端系统(负责提交用户身份信息的系统) * * 思考:对于一个认证授权系统来讲,需要什么?: * 1)提供一个认证的入口?(客户端去哪里认证) * 2)客户端应该携带什么信息去认证?(username,password,....) * 3)服务端通过谁去对客户端进行认证(一个负责认证的对象)? */ @AllArgsConstructor @Configuration @EnableAuthorizationServer //在oauth2规范中启动认证和授权 public class Oauth2Config extends AuthorizationServerConfigurerAdapter { //@Autowired private AuthenticationManager authenticationManager; //@Autowired private BCryptPasswordEncoder passwordEncoder; //@Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; //@Autowired private TokenStore tokenStore; //提供一个认证的入口(客户端去哪里认证)?(http://ip:port/.....) @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { //super.configure(security); //对外发布认证入口(/oauth/token),认证通过服务端会生成一个令牌 security.tokenKeyAccess("permitAll()") //对外发布检查令牌的入口(/oauth/check_token) .checkTokenAccess("permitAll()") //允许用户通过表单方式提交认证,完成认证 .allowFormAuthenticationForClients(); } //定义客户端应该携带什么信息去认证? //指明哪些对象可以到这里进行认证(哪个客户端对象需要什么特点)。 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //super.configure(clients); clients.inMemory() //客户端标识 .withClient("gateway-client") //客户端密钥(随意) .secret(passwordEncoder.encode("123456")) //指定认证类型(码密,刷新令牌,三方令牌,...) edGrantTypes("password","refresh_token") //作用域(在这里可以理解为只要包含我们规定信息的客户端都可以进行认证) .scopes("all"); } //提供一个负责认证授权的对象?(完成客户端认证后会颁发令牌,默认令牌格式是uuid方式的) @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { //super.configure(endpoints); //设置认证授权对象 endpoints.authenticationManager(authenticationManager) //设置令牌业务对象(此对象提供令牌创建及有效机制设置) .tokenServices(tokenService())//不写,默认是uuid //设置允许对哪些请求方式进行认证(默认支支持post):可选 .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); } @Bean public AuthorizationServerTokenServices tokenService(){ //1.构建token业务对象 DefaultTokenServices ts=new DefaultTokenServices(); //2.设置令牌生成机制(创建6牌的方式,存储用户状态信息的方式) ts.setTokenStore(tokenStore); //3.设置令牌增强(改变默认令牌创建方式,没有这句话默认是UUID) ts.setTokenEnhancer(jwtAccessTokenConverter); //4.设置令牌有效时长(可选) ts.setAccessTokenValiditySeconds(3600); //5.设置刷新令牌以及它的有效时时长(可选) ts.setSupportRefreshToken(true); ts.setRefreshTokenValiditySeconds(3600*24); return ts; } }
启动postman进行访问测试
-
登陆访问测试
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pIqSJ2NS-1645616815916)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/8c8001d2e8da4af98e7632bbd9013e45-164139005366218.png)]
-
检查token信息
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JkIscRWt-1645616815916)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/1cd2eb58c1f64b21a4d1b5e9b375a002-164139007725120.png)]
-
假如,请求访问ok,在postman控制台会显示如下格式信息
-
{ "user_name": "admin", "scope": [ "all" ], "active": true, "exp": 1635680023, "authorities": [ "sys:res:create", "sys:res:list", "sys:res:delete" ], "jti": "ce4aaee8-031f-4ff8-a0fe-e0cd93e8f374", "client_id": "gateway-client" }
-
刷新令牌应用测试
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-puGpCRd3-1645616815917)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/512da92c2555432db80438d78fed6d62-164139011291822.png)]
六:resource(资源服务)
1.业务描述
- 资源服务工程为一个业务数据工程,此工程中数据在访问通常情况下是受限访问,例如有些资源有用户,都可以方法,有些资源必须认证才可访问,有些资源认证后,有权限才可以访问。
2.业务设计架构
用户访问资源时的认证,授权流程设计如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5mKqo6Xg-1645616815917)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/fc8aae0e7bd64efc93673a9ebce14221-164139023166424.png)]
3.项目创建
第一步:创建工程
第二步:初始化pom文件依赖
-
<dependencies> <!--spring boot web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--nacos discovery--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--nacos config--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!--在资源服务器添加此依赖,只做授权,不做认证,添加完此依赖以后, 在项目中我们要做哪些事情?对受限访问的资源可以先判断是否登录了, 已经认证用户还要判断是否有权限? --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> </dependencies>
第三步:创建bootstrap.yml配置文
-
server: port: 8881 spring: application: name: sso-resource cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848 file-extension: yml
第四步:创建启动类
-
package com.jt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceApplication { public static void main(String[] args) { SpringApplication.run(ResourceApplication.class,args); } }
4.创建资源Controller对象
-
package com.jt.resource.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/resource") public class ResourceController { /** @PreAuthorize 设置访问时需要验证权限 * 查询资源 * @return */ @PreAuthorize("hasAuthority('sys:res:list')") @GetMapping public String doSelect(){ return "Select Resource ok"; } /** * 创建资源 * @return */ @PreAuthorize("hasAuthority('sys:res:create')") @PostMapping public String doCreate(){ return "Create Resource OK"; } /** * 修改资源 * @return */ @PreAuthorize("hasAuthority('sys:res:update')") @PutMapping public String doUpdate(){ return "Update Resource OK"; } /** * 删除资源 * @return */ @DeleteMapping public String doDelete(){ return "Delete resource ok"; } }
5.配置令牌解析器对象
-
package com.jt; 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 TokenConfig { /** * 配置令牌的存储策略,对于oauth2规范中提供了这样的几种策略 * 1)JdbcTokenStore(这里是要将token存储到关系型数据库) * 2)RedisTokenStore(这是要将token存储到redis数据库-key/value) * 3)JwtTokenStore(这里是将产生的token信息存储客户端,并且token * 中可以以自包含的形式存储一些用户信息) * 4).... */ @Bean public TokenStore tokenStore(){ //这里采用JWT方式生成和存储令牌信息 return new JwtTokenStore(jwtAccessTokenConverter()); } /** * 配置令牌的创建及验签方式 * 基于此对象创建的令牌信息会封装到OAuth2AccessToken类型的对象中 * 然后再存储到TokenStore对象,外界需要时,会从tokenStore进行获取。 */ @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter jwtAccessTokenConverter= new JwtAccessTokenConverter(); //JWT令牌构成:header(签名算法,令牌类型),payload(数据部分),Signing(签名) //这里的签名可以简单理解为加密,加密时会使用header中算法以及我们自己提供的密钥, //这里加密的目的是为了防止令牌被篡改。(这里密钥要保管好,要存储在服务端) jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);//设置密钥 return jwtAccessTokenConverter; } /** * JWT 令牌签名时使用的密钥(可以理解为盐值加密中的盐) * 1)生成的令牌需要这个密钥进行签名 * 2)获取的令牌需要使用这个密钥进行验签(校验令牌合法性,是否被篡改过) */ private static final String SIGNING_KEY="auth"; }
6.配置资源认证授权规则
-
package com.jt; import org.springframework.beans.factory.annotation.Autowired; 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.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; /** * 思考?对于一个系统而言,它资源的访问权限你是如何进行分类设计的 * 1)不需要登录就可以访问(例如12306查票) * 2)登录以后才能访问(例如12306的购票) * 3)登录以后没有权限也不能访问(例如会员等级不够不让执行一些相关操作) */ @Configuration @EnableResourceServer //启动方法上的权限控制,需要授权才可访问的方法上添加@PreAuthorize等相关注解 @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { //super.configure(http); //1.关闭跨域攻击 http.csrf().disable(); //2.放行相关请求 http.authorizeRequests() .antMatchers("/resource/**") .authenticated() .anyRequest().permitAll(); } }
7.启动Postman进行访问测试
不携带令牌访问
携带令牌访问
没有访问权限
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-225A04ht-1645616815918)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/image-20220109155222332-164171474350969.png)]
注意:更改没有权限,所以put提交会报403异常;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rZo92dGZ-1645616815918)(C:/Users/86131/Desktop/%E5%AD%A6%E4%B9%A0%E5%9B%BE%E5%BA%93/image-20220109155132856-164171469387468.png)]
七:网关服务
1.业务描述
- 本次设计中,API网关是服务访问入口,身份认证,资源访问都通过网关进行资源统一转发。
2.项目创建及初始化
第一步:创建项目
第二步:初始化pom文件内容
-
<dependencies> <!--网关服务依赖(底层基于netty技术和webflux)--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--服务的注册--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- 服务的配置--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--服务的限流依赖(网关限流依赖有2个)--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> </dependency> </dependencies>
第三步:创建bootstrap.yml配置文件并进行路由定义
-
server: port: 9000 spring: application: name: sso-gateway cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848 file-extension: yml sentinel: transport: dashboard: localhost:8180 eager: true gateway: routes: - id: router01 #资源服务器路由 uri: lb://sso-resource #lb表示负载均衡 predicates: #谓词对象 可以定义多个谓词逻辑 所有的谓词逻辑返回值为TRUE才会执行filter链 - Path=/sso/resource/** filters: #过滤器 谓词逻辑的下一个执行步骤 - StripPrefix=1 #去掉path中的第一层目录 - id: router02 #认证服务器路由 uri: lb://sso-auth predicates: - Path=/sso/oauth/** filters: - StripPrefix=1 globalcors: #跨域配置(针对AJAX请求,写到配置文件的好处是可以将其配置写到配置中心) corsConfigurations: '[/**]': allowedOrigins: "*" allowedHeaders: "*" allowedMethods: "*" allowCredentials: true
第四步:定义启动类
-
package com.jt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ApiGatewayApplication { public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class, args); } }
3.启动postman进行访问测试
基于网关进行登陆访问测试
基于网关进行资源访问测试
八:UI服务
第一步:在resource目录下创建static目录
第二步:在static目录下创建登陆页面login.html
-
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <title>login</title> </head> <body> <div class="container"id="app"> <h3>Please Login</h3> <form> <div class="mb-3"> <label for="usernameId" class="form-label">Username</label> <input type="text" v-model="username" class="form-control" id="usernameId" aria-describedby="emailHelp"> </div> <div class="mb-3"> <label for="passwordId" class="form-label">Password</label> <input type="password" v-model="password" class="form-control" id="passwordId"> </div> <button type="button" @click="doLogin()" class="btn btn-primary">Submit</button> </form> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> var vm=new Vue({ el:"#app",//定义监控点,vue底层会基于此监控点在内存中构建dom树 data:{ //此对象中定义页面上要操作的数据 username:"", password:"" }, methods: {//此位置定义所有业务事件处理函数 doLogin() { //1.定义url let url = "http://localhost:9000/sso/oauth/token" //2.定义参数 let params = new URLSearchParams() params.append('username',this.username); params.append('password',this.password); params.append('client_id',"gateway-client"); params.append('client_secret',"123456"); params.append('grant_type',"password"); //3.发送异步请求 axios.post(url, params) .then((response) => {//ok alert("login ok") let result=response.data; console.log("result",result); //将返回的访问令牌存储到浏览器本地对象中 localStorage.setItem("accessToken",result.access_token); location.href="/resource.html"; //启动一个定时器,一个小时以后,向认证中心发送刷新令牌 }) .catch((e)=>{ console.log(e); }) } } }); </script> </body> </html>
第三步:打开浏览器进行访问测试
1.创建资源展现页面
第一步:在UI工程的static目录下创建resource.html
-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <h1>The Resource Page</h1> <button οnclick="doSelect()">查询我的资源</button> <button οnclick="doUpdate()">修改我的资源</button> </div> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> function doSelect(){ let url="http://localhost:9000/sso/resource"; //获取登录后,存储到浏览器客户端的访问令牌 let token=localStorage.getItem("accessToken"); //发送请求时,携带访问令牌 axios.get(url,{headers:{"Authorization":"Bearer "+token}}) .then(function (response){ alert("select ok") console.log(response.data); }) .catch(function (e){//失败时执行catch代码块 if(e.response.status==401){ alert("请先登录"); location.href="/login.html"; }else if(e.response.status==403){ alert("您没有权限") } console.log("error",e); }) } function doUpdate(){ let url="http://localhost:9000/sso/resource"; //获取登录后,存储到浏览器客户端的访问令牌 let token=localStorage.getItem("accessToken"); console.log("token",token); //发送请求时,携带访问令牌 axios.put(url,"",{headers:{"Authorization":"Bearer "+token}}) .then(function (response){ alert("update ok") console.log(response.data); }) .catch(function (e){//失败时执行catch代码块 console.log(e); if(e.response.status==401){ alert("请先登录"); location.href="/login.html"; }else if(e.response.status==403){ alert("您没有权限") } console.log("error",e); }) } </script> </body> </html>
第二步:打开浏览器进行访问测试(登陆前和登陆后检查点击如下按钮检测结果
lStorage.getItem(“accessToken”);
//发送请求时,携带访问令牌
axios.get(url,{headers:{“Authorization”:“Bearer “+token}})
.then(function (response){
alert(“select ok”)
console.log(response.data);
})
.catch(function (e){//失败时执行catch代码块
if(e.response.status401){
alert(“请先登录”);
location.href="/login.html";
}else if(e.response.status403){
alert(“您没有权限”)
}
console.log(“error”,e);
})
}
function doUpdate(){
let url=“http://localhost:9000/sso/resource”;
//获取登录后,存储到浏览器客户端的访问令牌
let token=localStorage.getItem(“accessToken”);
console.log(“token”,token);
//发送请求时,携带访问令牌
axios.put(url,””,{headers:{“Authorization”:"Bearer "+token}})
.then(function (response){
alert(“update ok”)
console.log(response.data);
})
.catch(function (e){//失败时执行catch代码块
console.log(e);
if(e.response.status401){
alert(“请先登录”);
location.href="/login.html";
}else if(e.response.status403){
alert(“您没有权限”)
}
console.log(“error”,e);
})
}
第二步:打开浏览器进行访问测试(登陆前和登陆后检查点击如下按钮检测结果
![img](https://i-blog.csdnimg.cn/blog_migrate/4e27ac50564b9c0a3a95dd403edef3a6.png)