spring-boot 整合 JWT+SpringSecurity 进行登录认证

简要

1、SpringSecurity简要

  • 认证 (你是谁)
  • 授权 (你能干什么)
  • 攻击防护 (防止伪造身份)
    其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。

2、JWT 简要

  • JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

  • JSON Web Token由三部分组成,它们之间用圆点(.)连接
    第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

3、本文代码执行流程图大致如下

画图有点费时间。画的不好,见谅,主要为代码执行流程
在这里插入图片描述

登录

在这里插入图片描述

认证

在这里插入图片描述

4、pom.xml

加入必备依赖

 <!-- spring-security 5.2.0-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--jwt-->
        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.61</version>
        </dependency>

一、配置SpringSecurity

类结构
在这里插入图片描述

1、SecurityUser

这里继承了UserAdmin (原用户类所有属性)和 UserDetails 权限判断属性( Security框架)

package com.ws.ldy.config.springSecurity.entity;

import com.ws.ldy.admin.model.entity.UserAdmin;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

/**
 * SecurityUser 权限判断类 (用户信息子类)
 */
@Data
public class SecurityUser extends UserAdmin implements UserDetails {

    private static final long serialVersionUID = 1L;


    //账户是否未过期,过期无法验证,在springSecurity 验证中自动调用
    boolean isAccountNonExpired;

    //指定用户是否解锁,锁定的用户无法进行身份验证,在springSecurity 验证中自动调用
    boolean isAccountNonLocked;

    //指示是否已过期的用户的凭据(密码),过期的凭据防止认证,在springSecurity 验证中自动调用
    boolean isCredentialsNonExpired;

    //是否可用 ,禁用的用户不能身份验证,在springSecurity 验证中自动调用
    boolean isEnabled;

    //登录用户名
    private String username;

    //登录密码
    private String password;

    //权限列表
    private Collection<? extends GrantedAuthority> authorities;
}

2、XiJiaUserDetailsServiceImpl

继承 UserDetailsService ,登录认证方法,由SecurityConfig 来配置指定此类来认证

package com.ws.ldy.config.springSecurity.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ws.ldy.admin.mapper.AuthorityAdminMapper;
import com.ws.ldy.admin.mapper.UserAdminMapper;
import com.ws.ldy.admin.model.entity.AuthorityAdmin;
import com.ws.ldy.admin.model.entity.UserAdmin;
import com.ws.ldy.config.springSecurity.entity.SecurityUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.List;

/**
 * TODO 登录逻辑
 */
@Component
public class XiJiaUserDetailsServiceImpl implements UserDetailsService {

    // 用户表
    @Autowired
    private UserAdminMapper userAdminMapper;

    // 权限表
    @Autowired
    private AuthorityAdminMapper authorityAdminMapper;

    // 登录
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 账号查询( 账号必须唯一)
        UserAdmin userAdmin = userAdminMapper.selectOne(new LambdaQueryWrapper<UserAdmin>().eq(UserAdmin::getUsername, username));
        if (userAdmin == null) {
            return null;
        }

        // 账号密码及禁用过期等, // 状态 false, springSecurity 将验证失败,并返回不同的异常,失败方法根据不同异常返回不同的提示信息
        SecurityUser userDetail = userAdmin.convert(SecurityUser.class);
        userDetail.setUsername(userAdmin.getUsername());
        userDetail.setPassword(userAdmin.getPassword());
        userDetail.setAccountNonExpired(true);       // 是否过期
        userDetail.setAccountNonLocked(true);        // 是否解锁
        userDetail.setCredentialsNonExpired(true);   // 凭据(密码)是否过期
        userDetail.setEnabled(true);                 // 是否禁用

        // 查询权限并添加权限到userDetail
        List<AuthorityAdmin> userIdRoleAuthority = authorityAdminMapper.findUserIdRoleAuthority(userAdmin.getId());
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (AuthorityAdmin authority : userIdRoleAuthority) {
            authorities.add(new SimpleGrantedAuthority(authority.getUrl()));
        }
        userDetail.setAuthorities(authorities);
        return userDetail;
    }
}

3、SecurityConfig 配置类

package com.ws.ldy.config.springSecurity.config;

import com.ws.ldy.admin.mapper.AuthorityAdminMapper;
import com.ws.ldy.admin.model.entity.AuthorityAdmin;
import com.ws.ldy.config.jwt.filter.JWTLoginFilter;
import com.ws.ldy.config.jwt.filter.JWTValidFilter;
import com.ws.ldy.config.springSecurity.service.impl.XiJiaUserDetailsServiceImpl;
import com.ws.ldy.common.utils.auth.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.servlet.HandlerExceptionResolver;

import java.util.List;

/**
 * TODO  TODO Security配置文件,项目启动时就加载了
 *
 * @date 2020/7/5 0005 20:44
 * @return
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 登录-认证方法->loadUserByUsername
    @Autowired
    private XiJiaUserDetailsServiceImpl xiJiaUserDetailsService;


    // 异常处理类,在 filter无法使用全局异常,在 .addFilter(new JWTValidFilter 中传递该对象过去,便于返回异常信息
    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

    // 当前系统权限表
    @Autowired
    private AuthorityAdminMapper authorityAdminMapper;


    /**
     * 认证
     *
     * @return
     */
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        //对默认的UserDetailsService进行覆盖
        authenticationProvider.setUserDetailsService(xiJiaUserDetailsService);
        authenticationProvider.setPasswordEncoder(new PasswordEncoder() {

            // 对密码MD5
            @Override
            public String encode(CharSequence rawPassword) {
                return MD5Util.encode((String) rawPassword);
            }

            // 判断密码是否正确, rawPassword 用户输入的密码,  encodedPassword 数据库DB的密码,当 XiJiaUserDetailsServiceImpl的loadUserByUsername方法执行完后执行
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                //String rawPass = MD5Util.encode((String) rawPassword);
                boolean result = rawPassword.equals(encodedPassword);
                return result;
            }
        });
        return authenticationProvider;
    }


    /**
     * TODO 授权
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 只拦截需要拦截的所有接口, 拦截数据库权限表中的所有接口
        List<AuthorityAdmin> authoritys = authorityAdminMapper.selectList(null);
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry eiur = http.authorizeRequests();
        authoritys.forEach((auth) -> {
            eiur.antMatchers(auth.getUrl()).hasAnyAuthority(auth.getUrl());
        });
        // 配置token验证及登录认证,过滤器
        eiur
                // 登录接口不需要权限控制,可删除,目前该接口不在权限列表中
                .antMatchers("/auth/login", "POST").permitAll()
                // 设置JWT过滤器
                .and()
                .addFilter(new JWTValidFilter(authenticationManager(), resolver))
                .addFilter(new JWTLoginFilter(authenticationManager(), resolver)).csrf().disable()
                // 剔除session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 开启跨域访问
        http.cors().disable();
        // 开启模拟请求,比如API POST测试工具的测试,不开启时,API POST为报403错误
        http.csrf().disable();
        // iframe 跳转错误处理 Refused to display 'url' in a frame because it set 'X-Frame-Options' to 'deny'
        http.headers().frameOptions().disable();
    }


    /**
     * There is no PasswordEncoder mapped for the id "null"
     * 原因:升级为Security5.0以上密码支持多中加密方式,恢复以前模式
     *
     * @return
     */
    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }
}

二、JWT 配置

1、登录 filter

package com.ws.ldy.config.jwt.filter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ws.ldy.config.error.ErrorException;
import com.ws.ldy.common.utils.auth.JwtUtil;
import com.ws.ldy.config.springSecurity.entity.SecurityUser;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;

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.ArrayList;
import java.util.HashMap;
import java.util.Map;

/***
 * TODO  登录  ===> POST请求( 账号:username=?, 密码:password=?)
 *
 * 登录会调用springSecurity的登录方法进行验证
 *<p>
 * ===== 登录成功
 * http状态status状态返回200,并且自定义响应状态code返回200,响应头存放token,key = token,value = jwt生成的token内容
 * ===== 登录失败
 * http状态status状态返回401,并且自定义响应状态code返回401,并提示对应的内容
 * ===== 权限不足
 *  http状态status状态返回403,并且自定义响应状态code返回403,并提示对应的内容
 * </p>
 * @author 王松
 * @mail 1720696548@qq.com
 * @date 2020/7/5 0005 17:29
 */
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {

    /**
     * 获取授权管理, 创建JWTLoginFilter时获取
     */
    private AuthenticationManager authenticationManager;

    /**
     * 异常处理类
     */
    private HandlerExceptionResolver resolver;


    /**
     * 创建JWTLoginFilter,构造器,定义后端登陆接口-【/auth/login】,当调用该接口直接执行 attemptAuthentication 方法
     *
     * @param authenticationManager
     */
    public JWTLoginFilter(AuthenticationManager authenticationManager, HandlerExceptionResolver resolver) {
        this.authenticationManager = authenticationManager;
        this.resolver = resolver;
        super.setFilterProcessesUrl("/auth/login");
    }


    /**
     * TODO 一旦调用登录接口 /auth/login,立即执行该方法
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
        SecurityUser user = null;
        try {
            user = new ObjectMapper().readValue(request.getInputStream(), SecurityUser.class);
        } catch (IOException e) {
            // e.printStackTrace();
            resolver.resolveException(request, response, null, new ErrorException(401, "没有传递对应的参数"));
            return null;
        }
        // 调用springSecurity的 XiJiaUserDetailsServiceImpl 的 loadUserByUsername 方法进行登录认证,传递账号密码
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
    }

    /**
     * TODO  一旦调用 springSecurity认证登录成功,立即执行该方法
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {

        // 生成jwt 放入 Header
        SecurityUser userEntity = (SecurityUser) authResult.getPrincipal();
        String jwtToken = JwtUtil.generateToken(userEntity);
        response.addHeader("token", jwtToken);

        // 响应
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        PrintWriter out = response.getWriter();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("code", 200);
        map.put("message", "登录成功!");
        out.write(JSON.toJSONString(map));
        out.flush();
        out.close();
    }


    /**
     * TODO 一旦调用 springSecurity认证失败 ,立即执行该方法
     *
     * @param request
     * @param response
     * @param ex
     * @throws IOException
     * @throws ServletException
     */
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) {
        if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
            resolver.resolveException(request, response, null, new ErrorException(401, "用户名或密码错误"));
        } else if (ex instanceof InternalAuthenticationServiceException) {
            resolver.resolveException(request, response, null, new ErrorException(401, "没有账号信息"));
        } else if (ex instanceof DisabledException) {
            resolver.resolveException(request, response, null, new ErrorException(401, "账户被禁用"));
        } else {
            resolver.resolveException(request, response, null, new ErrorException(401, "登录失败!"));
        }
    }
}

2、认证 filter

package com.ws.ldy.config.jwt.filter;

import com.ws.ldy.config.error.ErrorException;
import com.ws.ldy.common.utils.auth.JwtUtil;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;

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.List;


/**
 * TODO jwt --> token 信息认证
 *
 * @author 王松
 * @mail 1720696548@qq.com
 * @date 2020/7/5 0005 17:29
 */
@Slf4j
public class JWTValidFilter extends BasicAuthenticationFilter {

    // 异常处理类
    private HandlerExceptionResolver resolver;

    /**
     * SecurityConfig 配置中创建该类实例
     */
    public JWTValidFilter(AuthenticationManager authenticationManager, HandlerExceptionResolver resolver) {
        // 获取授权管理
        super(authenticationManager);
        // 获取异常处理类
        this.resolver = resolver;
        this.resolver = resolver;
    }


    /**
     * 拦截请求
     *
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("请求方式:{} 请求URL:{} ", request.getMethod(), request.getServletPath());
        // 获取token, 没有token直接放行
        String token = request.getHeader("token");
        if (StringUtils.isBlank(token) || "null".equals(token)) {
            super.doFilterInternal(request, response, chain);
            return;
        }
        // 有token进行权限验证
        List<SimpleGrantedAuthority> userAuthList = null;
        String username = null;
        try {
            //  权限列表
            userAuthList = JwtUtil.getUserAuth(token);
            //  获取账号
            username = JwtUtil.getUsername(token);
        } catch (SignatureException ex) {
            resolver.resolveException(request, response, null, new ErrorException(10000, "JWT签名与本地计算签名不匹配"));
            return;
        } catch (ExpiredJwtException ex) {
            resolver.resolveException(request, response, null, new ErrorException(10000, "登录过期"));
            return;
        } catch (Exception e) {
            resolver.resolveException(request, response, null, new ErrorException(10000, "JWT解析错误"));
            return;
        }
        //  添加账户的权限信息,和账号是否为空,然后保存到Security的Authentication授权管理器中
        if (StringUtils.isNotBlank(username) && userAuthList != null) {
            SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(username, null, userAuthList));
        }
        super.doFilterInternal(request, response, chain);
    }
}

3、JWT生成工具类

package com.ws.ldy.common.utils.auth;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.ws.ldy.config.springSecurity.entity.SecurityUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import java.util.Date;
import java.util.List;

/***
 * TODO  jwt 工具类
 * @author 王松
 * @mail 1720696548@qq.com
 * @date 2020/7/5 0005 19:13
 */
public class JwtUtil {

    // 主题
    private static final String SUBJECT = "xijia";

    // jwt的token有效期,
    //private static final long EXPIRITION = 1000L * 60 * 60 * 24 * 7;//7天
    private static final long EXPIRITION = 1000L * 60 * 30;   // 半小时

    // 加密key(黑客没有该值无法篡改token内容)
    private static final String APPSECRET_KEY = "xijia";

    // 用户url权限列表key
    private static final String AUTH_CLAIMS = "auth";

    /**
     * TODO  生成token
     *
     * @param user
     * @return java.lang.String
     * @date 2020/7/6 0006 9:26
     */
    public static String generateToken(SecurityUser user) {
        String token = Jwts
                .builder()
                // 主题
                .setSubject(SUBJECT)
                // 添加jwt自定义值
                .claim(AUTH_CLAIMS, user.getAuthorities())
                .claim("username", user.getUsername())
                .claim("userId", user.getId())
                .claim("head", user.getHead())
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRITION))
                // 加密方式,加密key
                .signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();
        return token;
    }


//    public static Claims checkJWT(String token) {
//        try {
//            final Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
//            return claims;
//        } catch (Exception e) {
//            e.printStackTrace();
//            return null;
//        }
//    }

    /**
     * 获取用户Id
     *
     * @param token
     * @return
     */
    public static String getUserId(String token) {
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        return claims.get("userId").toString();
    }


    /**
     * 获取用户名
     *
     * @param token
     * @return
     */
    public static String getUsername(String token) {
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        return claims.get("username").toString();
    }

    /**
     * 获取用户角色的权限列表, 没有返回空
     *
     * @param token
     * @return
     */
    public static List<SimpleGrantedAuthority> getUserAuth(String token) {
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        List auths = (List) claims.get(AUTH_CLAIMS);
        String json = JSONArray.toJSONString(auths);
        List<SimpleGrantedAuthority> grantedAuthorityList = JSON.parseArray(json, SimpleGrantedAuthority.class);
        return grantedAuthorityList;
    }

    /**
     * 是否过期
     *
     * @param token
     * @return
     */
    public static boolean isExpiration(String token) {
        Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
        System.out.println("过期时间: " + claims.getExpiration());
        return claims.getExpiration().before(new Date());
    }
}

三、系统错误配置

1、WebServerAutoConfiguration (系统异常转发)

package com.ws.ldy.config.error;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

@Configuration
public class WebServerAutoConfiguration {
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400");
        ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401");
        ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403");
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404");
        ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415");
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");
        factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500);
        return factory;
    }
}

2、ErrorController (返回前端错误信息)

我这里抛的自定义异常走全局异常类,可以自己修改一下

package com.ws.ldy.config.error;

import com.ws.ldy.base.controller.BaseController;
import com.ws.ldy.common.result.EnumUtils;
import com.ws.ldy.common.result.ResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * TODO  WebServerAutoConfiguration 转发异常转发过来的信息,返回 json参数
 *
 * @author 王松
 * @mail 1720696548@qq.com
 * @date 2019/11/19 9:43
 * 方式1、直接跳转到具体错误页面
 * 方式2、返回json格式数据,由前端处理
 */
@SuppressWarnings("all")
@Controller
@Slf4j
public class ErrorController extends BaseController {

    /**
     * TODO  方式2:系统错误返回json
     *
     * @param code 对应错误码,ErrorPageConfig配置
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @date 2019/11/18 21:14
     */
    @RequestMapping(value = "/error/{code}")
    @ResponseBody
    public void error(@PathVariable int code) {
        // 根据状态值查询对应的枚举
        ResultEnum errorConstantEnum = EnumUtils.getByCode(Integer.valueOf(code), ResultEnum.class);
        // 返回对应提示
        if (errorConstantEnum != null) {
            throw new ErrorException(errorConstantEnum);
        }
        //返回500错误
        throw new ErrorException(ResultEnum.SYS_ERROR_CODE_500);
    }
}

本文到此结束,如果觉得有用,动动小手点赞或关注一下呗,将不定时持续更新更多的内容…,感谢大家的观看!

Spring Boot和Spring Security是非常常用的Java框架和库,用于构建安全和可扩展的Web应用程序。JWT(JSON Web Token)是一种用于在网络应用间安全传递身份验证和声明信息的标准。 要在Spring Boot中使用Spring SecurityJWT,你需要进行以下步骤: 1. 添加依赖:在你的项目的pom.xml文件中添加Spring SecurityJWT的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 2. 创建JWT工具类:编写一个JWT工具类,用于生成和解析JWT。 ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.security.core.userdetails.UserDetails; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtUtil { private static final String SECRET_KEY = "your-secret-key"; private static final long EXPIRATION_TIME = 864_000_000; // 10 days public static String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static String extractUsername(String token) { return extractClaims(token).getSubject(); } public static Date extractExpiration(String token) { return extractClaims(token).getExpiration(); } private static Claims extractClaims(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } } ``` 3. 配置Spring Security:创建一个继承自WebSecurityConfigurerAdapter的配置类,用于配置Spring Security。 ```java import org.springframework.beans.factory.annotation.Autowired; 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.method.configuration.EnableGlobalMethodSecurity; 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.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } @Bean public PasswordEncoder passwordEncoder() { return new Pbkdf2PasswordEncoder(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } ``` 4. 创建UserDetails实现类:实现Spring Security的UserDetails接口,用于获取用户信息。 ```java import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; public class CustomUserDetails implements UserDetails { private String username; private String password; public CustomUserDetails(String username, String password) { this.username = username; this.password = password; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(() -> "ROLE_USER"); } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } // Other UserDetails methods... @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ``` 5. 创建认证和授权的Controller:创建一个RestController,用于处理用户认证和授权的请求。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @PostMapping("/login") public ResponseEntity<String> login(@RequestBody AuthRequest authRequest) { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()) ); UserDetails userDetails = userDetailsService.loadUserByUsername(authRequest.getUsername()); String token = JwtUtil.generateToken(userDetails); return ResponseEntity.ok(token); } } class AuthRequest { private String username; private String password; // getters and setters... } ``` 这样,你就可以在Spring Boot应用程序中使用Spring SecurityJWT来实现认证和授权了。当用户登录时,会生成一个JWT,并在以后的请求中使用该JWT进行身份验证。 请注意,以上代码只是一个简单的示例,你可能需要根据你的实际需求进行适当的修改和扩展。另外,确保保护敏感信息(如密钥)的安全。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值