springsecurity+jwt实现认证和授权


还能不动声色饮茶,踏碎这一场,盛世烟花


springsecurity+jwt实现认证和授权

记录一下,初学者

SpringSecurity

SpringSecurity是一个强大的可高度定制的认证和授权框架,对于Spring应用来说它是一套Web安全标准
springsecurity+jwt实现认证和授权_第1张图片

JWT

JWT是JSON WEB TOKEN的缩写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的

应用:身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。

JWT的组成

JWT token的格式:header.payload.signature

  • header(头部):中用于存放签名的生成算法
  • payload(荷载):中用于存放用户名、token的生成时间和过期时间
  • signature(签名):为以header和payload生成的签名,

这个博客对参数和方法描述的比较详细
解析token

JWT实现认证和授权的原理

  • 用户调用登录接口,登录成功后获取到JWT的token;
  • 之后用户每次调用接口都在http的header中添加一个叫Authorization的头,值为JWT的token;
  • 后台程序通过对Authorization头中信息的解码及数字签名校验来获取其中的用户信息,从而实现认证和授权。

token保存在客户端。

1.导入依赖

<dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-webartifactId>
 dependency>
 <dependency>
      <groupId>mysqlgroupId>
      <artifactId>mysql-connector-javaartifactId>
      <scope>runtimescope>
dependency>

<dependency>
      <groupId>com.baomidougroupId>
      <artifactId>mybatis-plus-boot-starterartifactId>
      <version>3.4.1version>
dependency>
<dependency>
     <groupId>org.projectlombokgroupId>
     <artifactId>lombokartifactId>
     <optional>trueoptional>
dependency>

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

<dependency>
     <groupId>io.jsonwebtokengroupId>
     <artifactId>jjwtartifactId>
     <version>0.9.0version>
dependency>
 
<dependency>
     <groupId>com.github.axetgroupId>
     <artifactId>kaptchaartifactId>
     <version>0.0.9version>
dependency>

application.properties

# JWT储存的请求头
jwt.tokenHeader=Authorization
# JWT加解密使用的密钥
jwt.secret=my-secret
# JWT 的超期限时间(60*60*24)
jwt.expiration=604800
# JWT 荷载中拿到的开头
jwt.tokenHead=Bearer

2.编写jwt工具类

主要用于一些对token的操作:

  • 根据用户信息 生成token
  • 从token 中获取登录 用户名
  • 验证token是否有效
  • 判断token 是否可以被刷新
  • 刷新token
  • 根据荷载生成token
  • 从token中获得荷载

JwtTokenUtil.java

import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil {
    //定义用户名和过期时间
    private static final String CLAIM_KEY_USERNAME="sub";
    private static final String CLAIM_KEY_CREATED="created";
    @Value("${jwt.secret}")
    private String secret;  // jwt 加解密使用的密钥
    @Value("${jwt.expiration}")
    private Long expiration;  //jwt 的过期时间 (60*60*24)24小时失效
    
    /**
     *1.根据用户信息生成token
     * 用户信息可以在security框架的UserDetails里面获取
     * @param userDetails
     * @return
     */
     public String generateToken(UserDetails userDetails){
        Map<String,Object> claims = new HashMap<>(); //准备存放token的容器(荷载)
        claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername()); //从security框架的UserDetails 中获取用户名
        claims.put(CLAIM_KEY_CREATED,new Date()); //创建时间为当前时间
        return generateToken(claims); //增加其他信息(本类内新建方法)
    }

	/**
     * 4. 从token 中获取登录用户名
     * @param token
     * @return
     */
     public String getUserNameFromToken(String token){
        String username;
        try {
            Claims claims = getClaimsFromToken(token); //根据token 获取荷载(本类内新建方法)
            username = claims.getSubject();  //通过荷载调用 getSubject方法,获取用户名
        } catch (Exception e) {
            username = null;   //有异常username为空
        }
        return username;
    }

	/**
     * 6. 验证token是否有效
     * a。判断token是否过期
     * b。token荷载中的用户名和userDetails中的用户名是否一致
     * @param token
     * @param userDetails
     * @return
     */
     public boolean validateToken(String token,UserDetails userDetails){
        String username = getUserNameFromToken(token); //从token中获取用户名
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);//从token中拿到的用户名和用户信息对比,并判断token没有过期
    }

	/**
     *9.判断token是否可以被刷新
     * 过期了,可以刷新。获取有效时间方法取反为过期
     * @param token
     * @return
     */
     public boolean canRefresh(String token){
     return !isTokenExpired(token);
     }

	/**
     * 10.刷新token过期时间
     * @param token
     * @return
     */
    public String refreshToken(String token){
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED,new Date()); //通过荷载设置创建时间改为当前时间 = 刷新token过期时间
       return generateToken(claims);  //生成token
    }

	/**
     * 7.判断token 是否失效
     * @param token
     * @return
     */
    private boolean isTokenExpired(String token) {

        Date expireDate = getExpirationDateFromToken(token);//获取token的失效时间
        return expireDate.before(new Date()); //如果token 过期时间在当前时间前面,有效。
    }

	/**
     * 8.从token中拿到荷载,在获得过期时间
     * @param token
     * @return
     */
    private Date getExpirationDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

	/**
     * 5。通过解析token得到荷载
     * @param token
     * @return
     */
     private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)    //与builder中签名方法signWith()对应,设置签名(密钥)的key
                    .parseClaimsJws(token)   解析令牌,
                    .getBody();        //得到解析好的荷载
             } catch (Exception e) {
            e.printStackTrace();
        }
        return claims;
    }

	/**
     * 2.根据荷载生成 JWT token
     * @param claims
     * @return 私有,供本类使用
     */
     private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)    //设置荷载
                .setExpiration(generateExpirationDate())  //设置过期时间 ,需要类型转换(本类内新建方法)
                .signWith(SignatureAlgorithm.ES512,secret)  //设置签名方法
                .compact();  //生成jwt
    }

	/**
     * 3.生成token 失效时间
     * 类型转换
     * @return
     */
    private Date generateExpirationDate() {
        //失效时间为:当前时间+配置的过期时间
        return new Date(System.currentTimeMillis() + expiration*1000);
    }
}

3.编写SecurityConfig.java配置类

这个也是最核心的一个配置了。这里简单说明一下重写的3个config。
1.protected void configure(AuthenticationManagerBuilder auth)

AuthenticationManagerBuilder(身份验证管理生成器)

用来配置认证管理器AuthenticationManager。说白了就是用于配置UserDetailsService及PasswordEncoder;


2.public void configure(WebSecurity web)

WebSecurity(WEB安全)

用来配置 WebSecurity 。而 WebSecurity 是基于 Servlet Filter 用来配置 springSecurityFilterChain 。而 springSecurityFilterChain 又被委托给了 Spring Security 核心过滤BeanDelegatingFilterProxy
我们一般不会过多来自定义 WebSecurity , 使用较多的使其ignoring() 方法用来忽略 Spring Security 对静态资源的控制。


3.protected void configure(HttpSecurity http)

HttpSecurity(HTTP请求安全处理)

HttpSecurity 用于构建一个安全过滤器链 SecurityFilterChain 。SecurityFilterChain 最终被注入核心过滤器 。 HttpSecurity 有许多我们需要的配置。我们用于配置需要拦截的url路径、jwt过滤器及出异常后的处理器
常用方法

方法 说明
openidLogin() 用于基于 OpenId 的验证
headers() 将安全标头添加到响应,比如说简单的 XSS 保护
cors() 配置跨域资源共享( CORS )
sessionManagement() 允许配置会话管理
portMapper() 允许配置一个PortMapper(HttpSecurity#(getSharedObject(class))),其他提供SecurityConfigurer的对象使用 PortMapper 从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security使用一个PortMapperImpl映射 HTTP 端口8080到 HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443
jee() 配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理
x509() 配置基于x509的认证
rememberMe() 允许配置“记住我”的验证
authorizeRequests() 允许基于使用HttpServletRequest限制访问
requestCache() 允许配置请求缓存
exceptionHandling() 允许配置错误处理
securityContext() 在HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。 当使用WebSecurityConfigurerAdapter时,这将自动应用
servletApi() 将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。 当使用WebSecurityConfigurerAdapter时,这将自动应用
csrf() 添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用
logout() 添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success”
anonymous() 允许配置匿名用户的表示方法。 当与WebSecurityConfigurerAdapter结合使用时,这将自动应用。 默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,并包含角色 “ROLE_ANONYMOUS”
formLogin() 指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面
oauth2Login() 根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证
requiresChannel() 配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射
httpBasic() 配置 Http Basic 验证
addFilterBefore() 在指定的Filter类之前添加过滤器
addFilterAt() 在指定的Filter类的位置添加过滤器
addFilterAfter() 在指定的Filter类的之后添加过滤器
and() 连接以上策略的连接器,用来组合安全策略。实际上就是"而且"的意思

还有一些相关方法的说明:

  • RestfulAccessDeniedHandler:当用户没有访问权限时的处理器,用于返回JSON格式的处理结果;
  • RestAuthenticationEntryPoint:当未登录或token失效时,返回JSON格式的结果;
  • UserDetailsService:SpringSecurity定义的核心接口,用于根据用户名获取用户信息,需要自行实现;
  • UserDetails:SpringSecurity定义用于封装用户信息的类(主要是用户信息和权限),需要自行实现;
  • PasswordEncoder:SpringSecurity定义的用于对密码进行编码及比对的接口,目前使用的是BCryptPasswordEncoder;
  • JwtAuthenticationTokenFilter:在用户名和密码校验前添加的过滤器,如果有jwt的token,会自行根据token信息进行登录。
import com.xxx.server.config.security.component.*;
import com.xxx.server.pojo.Admin;
import com.xxx.server.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AdminService adminService;

    @Autowired
    private RestAuthorizationEntryPoint restAuthorizationEntryPoint; // 未登录 token 失效时自定义处理结果

	@Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler; // 无权访问时自定义处理结果

    @Autowired
    private CustomFilter customFilter;

    @Autowired
    private CustomUrlDecisionManager customUrlDecisionManager;

	// 1、重写 UserDetailsService,用我们自己写的业务逻辑
    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            Admin admin = adminService.getAdminByUserName(username);
            if (null != admin) {
            admin.setRoles(adminService.getRoles(admin.getId()));
                return admin;
            }
            throw new UsernameNotFoundException("用户名或密码不正确");
        };
    }
    
    // 2、让 Security 走我们重写的 UserDetailsService ,通过 getAdminByUserName 获取用户名
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    }
    
     // 3、密码加解密对象
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    // 6、放行路径(不走拦截链)
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/**.html",
                "/login/**",
                "/hello/**",
                "/logout/**",
                "/css/**",
                "/js/**",
                "/img/**",
                "/captcha",      // 验证码接口
        );
    }
     /**
     * 4、SpringSecurity 配置
     *
     * @param http
     * @throws Exception
     */
     /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */

      @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 使用 JWT , 不需要 csrf
        http.csrf()
                .disable()
                // 基于 token,不需要 session
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 所有请求都要求认证
                .anyRequest()
                .authenticated()
                // 动态权限配置
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(customUrlDecisionManager);//权限决策处理类
                        object.setSecurityMetadataSource(customFilter);//路径(资源)拦截处理
                        return object;
                    }
                })
                .and()
                // 禁用缓存
                .headers()
                .cacheControl();

		// 添加 jwt 登录授权过滤器
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        // 添加自定义未授权和未登录结果返回
        http.exceptionHandling()
        .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthorizationEntryPoint);
    }
 
 // 5、JWT 登录授权过滤器
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
}               

这里面的动态权限配置,jwt登录授权过滤器,未授权未登录情况的处理都需要我们自己配置

1.决策权限处理

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import java.util.Collection;

/**
 * 权限控制
 * 判断用户角色
 *
 * @author 
 */
@Component
public class CustomUrlDecisionManager implements AccessDecisionManager {

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        for (ConfigAttribute configAttribute : configAttributes) {
            //当前url所需要的角色
            String needRole = configAttribute.getAttribute();
            if ("ROLE_LOGIN".equals(needRole)){
                if (authentication instanceof AnonymousAuthenticationToken){
                    throw new AccessDeniedException("尚未登录,请登录");
                }else {
                    return;
                }
            }
             Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)){
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足,请联系管理员");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return false;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return false;
    }
}

2.路径(资源)拦截处理

import com.xxx.server.pojo.Menu;
import com.xxx.server.pojo.Role;
import com.xxx.server.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import java.util.Collection;
import java.util.List;

/** 权限控制
 *  根据请求 url 分析请求所需的角色
 * @author  
 */
 @Component
public class CustomFilter implements FilterInvocationSecurityMetadataSource {

    @Autowired
    private MenuService menuService;

    AntPathMatcher antPathMatcher = new AntPathMatcher();

	@Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 获取请求的 url
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        // 权限角色查询所有菜单
        List<Menu> menus = menuService.getMenusWithRole();
        // 判断请求 url 与菜单角色是否匹配
        for (Menu menu : menus) {
            if (antPathMatcher.match(menu.getUrl(),requestUrl)) {
                String[] str = menu.getRoles().stream().map(Role::getName).toArray(String[]::new);
                return SecurityConfig.createList(str);
            }
        }
        // 没匹配的 url 默认登录即可访问 : ROLE_LOGIN 登录即可拥有的角色
        return SecurityConfig.createList("ROLE_LOGIN");
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
	 @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}


3.添加 jwt 登录授权过滤器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
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;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Value("${jwt.tokenHeader}")
    private String tokenHeader; // JWT 存储的请求头( key )
    @Value("${jwt.tokenHead}")
    private String tokenHead; // JWT 负载中拿到开头( value )
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserDetailsService userDetailsService;
     // 前置拦截器
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String authHeader = request.getHeader(tokenHeader); // 根据 key 获取 value(要验证的头)
        // 存在 token
        // 如果拿到 value,并且是根据 Bearer 开头的
        if (null != authHeader && authHeader.startsWith(tokenHead)) {
            // 截取字符串
            String authToken = authHeader.substring(tokenHead.length());
            String username = jwtTokenUtil.getUserNameFromToken(authToken);
            // token 存在用户名,但未登录( 在 springSecurity 上下文拿不到 用户对象 )
            if (null != username && null == SecurityContextHolder.getContext().getAuthentication()) {
                // 登录(通过 username 拿到 UserDetails )
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                // 判断 token 是否有效,重新设置用户对象
                if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                // 参数:用户对象 密码 角色
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                // 重新设置用户对象到 springSecurity上下文中去
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
        // 放行
        filterChain.doFilter(request, response);
    }
}
                   

4.访问接口无权限,自定义返回结果

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxx.server.utils.RespBean;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter out = response.getWriter();
        RespBean bean = RespBean.error("权限不足,请联系管理员!");
        bean.setCode(403);
        out.write(new ObjectMapper().writeValueAsString(bean));
        out.flush();
        out.close();
         }
}

5.未登录或者token失效时访问接口

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxx.server.utils.RespBean;
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;
import java.io.PrintWriter;

@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException e) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter out = response.getWriter();
        RespBean bean = RespBean.error("您尚未登录,请登录!");
        bean.setCode(401);
        out.write(new ObjectMapper().writeValueAsString(bean));
        out.flush();
        out.close();

    }
}

你可能感兴趣的:(jwt,安全,spring,boot)