Spring Boot整合Spring Security+Jwt +Redis 实现前后端分离安全框架

完整的pom文件

主要引入了security、redis、jpa和jjwt的相关依赖。

<?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.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.security</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </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>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    </dependencies>

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

</project>

完整的配置文件

application.yml全局配置文件。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456

  jpa:
    database: mysql
    show-sql: true
    hibernate:
      ddl-auto: update

  redis:
    host: localhost
    port: 6379
    password:
    database: 0           #Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
    timeout: 5000         #连接超时时间(ms)

jwt:
  #签名
  signingKey: fucker
  #过期时间(单位:秒)
  expiration: 600

实体类

采用RBAC模式建立数据库。RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这样的方式管理层级相互依赖,权限赋予给角色,而把角色又赋予用户,用户量不大的时候管理起来很方便。

权限实体类。

package com.security.bean;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
@Data
public class Permission {
    @Id @GeneratedValue
    private long id;//主键.
    private String url;//授权链
}

角色实体类。

package com.security.bean;

import lombok.Data;

import javax.persistence.*;
import java.util.List;

@Entity
@Data
public class Role {
    @Id @GeneratedValue
    private long rid;//主键.
    private String name;//角色名称.
    private String description;//角色描述.

    // 角色 - 权限是多对多的关系
    @ManyToMany(fetch= FetchType.EAGER)
    @JoinTable(name="RolePermission",joinColumns= {@JoinColumn(name="role_id")} , inverseJoinColumns= {@JoinColumn(name="permission_id")})
    private List<Permission> permissions;
}

用户实体类。

package com.security.bean;

import lombok.Data;

import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;

@Entity
@Data
public class UserInfo {

    @Id @GeneratedValue
    private long uid;//主键.
    private String username;//用户名.
    private String password;//密码.

    //用户--角色:多对多的关系.
    @ManyToMany(fetch=FetchType.EAGER)//立即从数据库中进行加载数据;
    @JoinTable(name = "UserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns ={@JoinColumn(name = "role_id") })
    private List<Role> roles;
}

Repository

用于查询数据库(使用jpa就是这么简单)。

package com.security.repository;

import com.security.bean.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {

    UserInfo findByUsername(String username);
}

Token工具类

主要是对redis和jwt的操作,使用redis实现登录、登出动态管理。

package com.security.service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Data
@Component
@ConfigurationProperties(prefix = "jwt")
public class TokenServiceImpl {
    public Integer expiration;
    public String signingKey;

    final StringRedisTemplate redisTemplate;

    public TokenServiceImpl(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public String createToken(String username, Collection<? extends GrantedAuthority> authorities){
        String jwtToken=Jwts.builder()
			                //存入用户权限信息
			                .claim("authorities", authorities)
			                .setSubject(username)
			                .setExpiration(new Date(System.currentTimeMillis() + expiration*1000))
			                .signWith(SignatureAlgorithm.HS512,signingKey)
			                .compact();
		//将jwtToken存入redis	                
        if(set(jwtToken,jwtToken)){
            return jwtToken;
        }else {
            return null;
        }
    }

    /**
     *  获取存入jwt的个人变量authorities。
     */
    public Collection<GrantedAuthority> getAuthority(Claims claims){
        Collection<GrantedAuthority> authorities=new ArrayList<>();
        String[] as=claims.get("authorities").toString().replace("[","").replace("]","").split(",");
        for (String a:as){
            authorities.add(new SimpleGrantedAuthority(a));
        }
        return authorities;
    }

    /**
     *  解析redis中存的jwtToken
     */
    public Claims parseToken(String jwtToken){
        String token = get(jwtToken.replace("Bearer","").trim());
        Claims claims =null;
        try {
            claims = Jwts.parser()
	                     .setSigningKey(signingKey)
	                     .parseClaimsJws(token)
	                     .getBody();
        }catch (Exception e){
            e.printStackTrace();
        }
        return claims;
    }
    
    /**
     * 下面的方法对redis进行操作,包括存、取和删。
     */
    private boolean set(String key, String value) {
        try {
            redisTemplate.opsForValue().set(key, value,expiration, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private String get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    public boolean del(String key) {
        try {
            redisTemplate.delete(key.replace("Bearer","").trim());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

过滤类

匿名未登录访问的时执行。

package com.security.filter;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 匿名未登录访问的时执行
 */
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        //设置response状态码,返回错误信息等
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(e.getMessage());
    }
}

没有权限,被拒绝访问时执行。

package com.security.filter;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 没有权限,被拒绝访问时执行
 */
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        //设置response状态码,返回错误信息等
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(accessDeniedException.getMessage());
    }
}

登录过滤,请求登录接口后进入attemptAuthentication方法,登录成功执行successfulAuthentication方法,登录失败执行unsuccessfulAuthentication方法。

package com.security.filter;

import com.security.service.TokenServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
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.Collection;

public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {

    @Autowired
    TokenServiceImpl tokenService;

    public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {
        super(new AntPathRequestMatcher(defaultFilterProcessesUrl));
        setAuthenticationManager(authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse resp) throws AuthenticationException, IOException, ServletException {
        System.err.println("__________________________________________________");
        //从请求参数中获取用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //该方法会去调用CustomUserDetailServiceImpl.loadUserByUsername
        return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(username, password));
    }

    /**
     * 登录成功处理
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse resp, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //获取用户角色和用户名
        Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
        String username=authResult.getName();
        //创建token
        String jwt =tokenService.createToken(username,authorities);
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter out = resp.getWriter();
        out.write(jwt);
        out.flush();
        out.close();
    }

    /**
     * 登录失败处理
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest req, HttpServletResponse resp, AuthenticationException failed) throws IOException, ServletException {
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter out = resp.getWriter();
        out.write(failed.getMessage());
        out.flush();
        out.close();
    }
}

请求过滤,主要进行token验证,然后将认证对象放入security上下文。

package com.security.filter;

import com.security.service.TokenServiceImpl;
import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;

@Component
public class JwtFilter extends OncePerRequestFilter {
    private final TokenServiceImpl tokenService;

    public JwtFilter(TokenServiceImpl tokenService) {
        this.tokenService = tokenService;
    }

    /**
     * 验证authorization
     */
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        System.err.println("==================================================");
        String jwtToken = httpServletRequest.getHeader("authorization");
        System.out.println(jwtToken);
        //解析authorization
        Claims claims = tokenService.parseToken(jwtToken);
        if(claims!=null){
            //获取当前登录用户名//获取用户角色
            String username = claims.getSubject();
            Collection<GrantedAuthority> authorities=tokenService.getAuthority(claims);

            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }
}

退出成功后执行,删除redis中的token。

package com.security.filter;

import com.security.service.TokenServiceImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义退出处理类 返回成功
 */
@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
    private final TokenServiceImpl tokenService;

    public CustomLogoutSuccessHandler(TokenServiceImpl tokenService) {
        this.tokenService = tokenService;
    }

    /**
     * 退出处理
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException{

        String jwtToken = request.getHeader("authorization");
        String username = tokenService.parseToken(jwtToken).getSubject();
        tokenService.del(jwtToken);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(username + "退出成功。。。");
    }
}

业务类

登录会自动调用此方法,将登录用户对象返回给security。

package com.security.service;

import com.security.bean.Permission;
import com.security.bean.Role;
import com.security.bean.UserInfo;
import com.security.repository.UserInfoRepository;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Component;

import java.util.ArrayList;
import java.util.Collection;

@Component
public class CustomUserDetailServiceImpl implements UserDetailsService{

    private final UserInfoRepository userInfoRepository;

    public CustomUserDetailServiceImpl(UserInfoRepository userInfoRepository) {
        this.userInfoRepository = userInfoRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++");
        //通过username获取用户信息
        UserInfo userInfo = userInfoRepository.findByUsername(username);
        if(userInfo == null) {
            throw new UsernameNotFoundException("not found");
        }

        //定义权限列表.
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        for(Role role:userInfo.getRoles()){
            for (Permission perm:role.getPermissions()){
                authorities.add(new SimpleGrantedAuthority(perm.getUrl()));
            }
        }

        User userDetails = new User(username,userInfo.getPassword(),authorities);
        System.out.println("当前用户拥有权限:"+userDetails.getAuthorities());
        return userDetails;
    }
}

在这里面进行权限验证(还可以补充更多的方法)。

package com.security.service;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

/**
 * 权限验证类
 */
@Component
public class PermissionServiceImpl {
    final TokenServiceImpl tokenService;

    public PermissionServiceImpl(TokenServiceImpl tokenService) {
        this.tokenService = tokenService;
    }

    /**
     * 验证用户是否具备某权限
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public boolean hasPermission(String permission){
        System.out.println("..................................................");
        if (StringUtils.isEmpty(permission)){
            return false;
        }

        //获取当前登录用户的jwtToken。
        String jwtToken = getRequest().getHeader("authorization");
        System.out.println(jwtToken);
        //直接从jwt中解析用户权限,避免重复查询数据库。
        Collection<GrantedAuthority> authorities=tokenService.getAuthority(tokenService.parseToken(jwtToken));

        //封装当前用户拥有的权限
        Collection<String> permissions=new ArrayList<>();
        for(GrantedAuthority authority:authorities){
            String perm=authority.getAuthority().split("=")[1].replace("}","");
            permissions.add(perm);
        }
        System.out.println("当前需要权限:"+permission);
        System.out.println("当前用户权限:"+permissions);
        return permissions.contains(permission);
    }

    private HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    }
}

配置类

主要配置了加密方式和过滤器。

package com.security.config;

import com.security.filter.*;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
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;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

//添加@EnableGlobalMethodSecurity注解开启Spring方法级安全
// prePostEnabled 决定Spring Security的前注解是否可用 [@PreAuthorize,@PostAuthorize,..],设置为true
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    private final CustomLogoutSuccessHandler customLogoutSuccessHandler;
    private final CustomAccessDeniedHandler customAccessDeniedHandler;
    private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
    private final JwtFilter jwtFilter;

    public WebSecurityConfig(CustomLogoutSuccessHandler customLogoutSuccessHandler, CustomAccessDeniedHandler customAccessDeniedHandler, CustomAuthenticationEntryPoint customAuthenticationEntryPoint, JwtFilter jwtFilter) {
        this.customLogoutSuccessHandler = customLogoutSuccessHandler;
        this.customAccessDeniedHandler = customAccessDeniedHandler;
        this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
        this.jwtFilter = jwtFilter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
            //登录后,访问没有权限处理类
            .exceptionHandling().accessDeniedHandler(customAccessDeniedHandler)
            //匿名访问,没有权限的处理类
            .authenticationEntryPoint(customAuthenticationEntryPoint)
            //退出登录
            .and()
            .logout().logoutUrl("/logout").logoutSuccessHandler(customLogoutSuccessHandler)

            .and()
            .authorizeRequests()
            .antMatchers(HttpMethod.POST, "/login").permitAll()
            .anyRequest().authenticated()

            //无状态登录,取消session管理
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

            .and()
            //登录请求过滤
            .addFilterBefore(jwtLoginFilter(),UsernamePasswordAuthenticationFilter.class)
            //验证authorization过滤
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public JwtLoginFilter jwtLoginFilter() throws Exception {
        return new JwtLoginFilter("/login",authenticationManager());
    }

    /**
     * 指定加密方式
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Controller

使用@PreAuthorize注解实现权限验证。
这里参考了若依的前后端分离框架 http://www.ruoyi.vip/(官方网站,值得拥有)。

package com.security.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @GetMapping("/user")
    @PreAuthorize("@permissionServiceImpl.hasPermission('helloUser')")
    public String hello() {
        return "hello user !";
    }

    @GetMapping("/admin")
    @PreAuthorize("@permissionServiceImpl.hasPermission('helloAdmin')")
    public String admin() {
        return "hello admin !";
    }
}

数据库

启动项目,jpa会自动帮我们生成表。运行单元测试添加两个用户,之后手动添加了一些角色权限信息。
这样用户user就只拥有helloUser权限,而用户admin拥有helloAdminhelloUser权限。
用户信息表。
在这里插入图片描述
角色信息表。
在这里插入图片描述
用户角色关系表。
在这里插入图片描述
权限信息表。
在这里插入图片描述
角色权限关系表。
在这里插入图片描述

添加用户

直接使用单元测试往数据库中添加两个用户(密码要加密)。

package com.security;

import com.security.bean.UserInfo;
import com.security.repository.UserInfoRepository;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
class DemoApplicationTests {

    @Autowired
    private UserInfoRepository userInfoRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Test
    void contextLoads() {
        UserInfo admin = new UserInfo();
        admin.setUsername("admin");
        admin.setPassword(passwordEncoder.encode("123456"));
        userInfoRepository.save(admin);

        UserInfo user = new UserInfo();
        user.setUsername("user");
        user.setPassword(passwordEncoder.encode("123456"));
        userInfoRepository.save(user);
    }
}

测试效果

使用Postman进行测试。Postman官网 https://www.postman.com/(各种请求,应有尽有)。
未登录
在这里插入图片描述

登录user用户

登录使用Post请求,用户名username,密码password
在这里插入图片描述
user用户允许访问/user接口(将登录接口返回的token放入Authorization栏的Bearer Token中)。
在这里插入图片描述
user用户禁止访问/admin接口。
在这里插入图片描述
登录admin用户
在这里插入图片描述
admin用户允许访问/user接口。
在这里插入图片描述
admin用户允许访问/admin接口。
在这里插入图片描述
退出登录
使用GET或者POST请求http://localhost:8080/logout,需要带上token。
在这里插入图片描述
最后附上代码资源链接 https://download.csdn.net/download/weixin_43424932/12195722

发布了82 篇原创文章 · 获赞 9 · 访问量 6175

猜你喜欢

转载自blog.csdn.net/weixin_43424932/article/details/104519094