Springboot3.0+Security6.0+jwt+jdk17登陆认证

集各位大佬的精华,弄出来的一套基于jdk17,Springboot3.0,SpringSecurity6.0,jwt的登陆及接口拦截体系。

1.先贴依赖:

 
     org.springframework.boot
     spring-boot-starter-parent
     3.0.0
      
 

   
        
            org.springframework.boot
            spring-boot-starter-jdbc
        
        
            org.springframework.boot
            spring-boot-starter-security
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.springframework.security
            spring-security-test
            test
        

        
        
            com.nimbusds
            nimbus-jose-jwt
            9.31
        
        
            org.projectlombok
            lombok
            true
        


        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.5.3.1
        
        
            com.baomidou
            mybatis-plus-generator
            3.5.3.1
            
                
                    mybatis
                    org.mybatis
                
            
        

        
            log4j
            log4j
            1.2.17
        

        
            junit
            junit
            4.12
            test
        
        
            com.alibaba.fastjson2
            fastjson2
            2.0.26
        


2.jwtUtil  



import com.alibaba.fastjson2.JSON;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.zy.common.ResponseEntity;
import com.zy.entity.security.Claims;

import java.text.ParseException;
import java.util.Date;
import java.util.UUID;

/**
 * @author : zy
 * JWT工具类
 */
public class JWTUtil {
    //密钥
    private static final String secret = "xpo1xgnl5ksinxkgu1nb6vcx3zaq1wsxvv";

    // 1000 * 60 * 60 * 24 * 1  一天
    //过期时间12h,单位毫秒
    private static final long EXPIRE = 1000 * 60 * 60 * 12;
    // 测试时为1min
    // private static final long EXPIRE = 1000 * 60 * 1;

    /**
     * 创建token
     *
     * @param claims 用户信息
     * @return 令牌
     */
    public static String createToken(Claims claims) {
        try {
            //对密钥进行签名
            JWSSigner jwsSigner = new MACSigner(secret);

            //准备JWS header
            JWSHeader jwsHeader = new JWSHeader
                    .Builder(JWSAlgorithm.HS256)
                    .type(JOSEObjectType.JWT)
                    .build();

            //准备JWS payload
            claims.setJti(UUID.randomUUID().toString());
            claims.setIat(new Date().getTime());
            claims.setExp(new Date(System.currentTimeMillis() + EXPIRE).getTime());

            Payload payload = new Payload(JSON.toJSONString(claims));

            //封装JWS对象
            JWSObject jwsObject = new JWSObject(jwsHeader, payload);

            //签名
            jwsObject.sign(jwsSigner);

            return jwsObject.serialize();

        } catch (KeyLengthException e) {
            e.printStackTrace();
        } catch (JOSEException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 验证并获取用户信息
     *
     * @param token 令牌
     * @return 解析后用户信息
     */
    public static ResponseEntity verifyToken(String token) {
        JWSObject jwsObject;
        ResponseEntity response = new ResponseEntity<>();
        try {
            jwsObject = JWSObject.parse(token);

            //HMAC验证器
            JWSVerifier jwsVerifier = new MACVerifier(secret);
            if (!jwsObject.verify(jwsVerifier)) {
                response.setCode(10008).setErrorMsg("token无效");
                return response;
            }

            String payload = jwsObject.getPayload().toString();
            Claims claims = JSON.parseObject(payload, Claims.class);
            if (claims.getExp() < new Date().getTime()) {
                response.setCode(10008).setErrorMsg("token无效");
                return response;
            }

            response.setCode(200).setData(claims).setMessage("解析成功");
            return response;
        } catch (ParseException | JOSEException e) {
            e.printStackTrace();
        }
        response.setCode(10008).setErrorMsg("token无效");
        return response;
    }
}

3.jwtAuthenticationFilter



import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zy.common.ResponseEntity;
import com.zy.config.security.JWTUtil;
import com.zy.entity.security.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.logging.Logger;

/**
 * @author : zy
 *
 * 自定义jwt全局过滤器
 * 1.没有携带token放行
 * 2.携带token,将用户信息添加至security上下文中
 */
@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter {
    private static final Logger logger = Logger.getLogger(JWTAuthenticationFilter.class.toString());

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取当前请求的uri
        String uri = request.getRequestURI();
        logger.info("请求路径:" + uri);
        //判断是否是认证请求路径
        //是:直接放行
        if (uri.endsWith("/auth/login") || uri.endsWith("/auth/logout") || uri.startsWith("/swagger-ui")
                || uri.endsWith("doc.html") || uri.startsWith("/webjars/css") || uri.startsWith("/webjars/js")
                || uri.startsWith("/v3/api-docs") || uri.startsWith("/favicon.ico")
                || uri.startsWith("**/*.html") || uri.endsWith("/webjars/springfox-swagger-ui")
                || uri.startsWith("/swagger-resources")) {
            filterChain.doFilter(request, response);
            return;
        }

        //否:获取请求头中携带的token
        String authorization = request.getHeader("Authorization");
        logger.info("携带authorization:" + authorization);

        //判断是否携带token
        //否:抛出异常
        if (StringUtils.isBlank(authorization)) {
            logger.info("未查询到token");
            return;
        }

        String realToken = authorization.replace("Bearer ", "");


        //是:校验jwt有效性
        ResponseEntity responseE = JWTUtil.verifyToken(realToken);
        Claims data = (Claims) responseE.getData();

        if (ObjectUtils.isEmpty(data)) {
            logger.info("token失效");
            return;
        }

        // 验证token对象是否存在及验证token是否过期
        if (ObjectUtils.isEmpty(data)) {
            logger.info("token无效或者已经失效");
            return;
        }
        if (responseE.getCode() != 200) {
            logger.info("token无效");
            return;
        }

        filterChain.doFilter(request, response);
    }
}

4.SecurityConfig

注:此处.requestMatchers("/**").permitAll()应该是把所有接口都放开,在jwtAuthenticationFilter中实现接口拦截,因为不写/**,其余接口直接访问不到,才疏学浅不知道为啥,大家可以试试,改正。


import com.zy.config.security.filter.JWTAuthenticationFilter;
import com.zy.entity.SysUser;
import com.zy.entity.security.LoginUser;
import com.zy.mapper.SysUserMapper;
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.authentication.AuthenticationProvider;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author zy
 */
@Configuration
public class SecurityConfig {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private JWTAuthenticationFilter jwtAuthenticationFilter;


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            SysUser user = sysUserMapper.selectByName(username);
            if (user == null) {
                throw new UsernameNotFoundException("用户不存在");
            }
            Integer status = user.getStatus();
            if (status != 0){
                throw new LockedException("用户已停用");
            }
            return new LoginUser(user);
        };
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        authenticationProvider.setHideUserNotFoundExceptions(false);
        return authenticationProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(authenticationProvider());
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authenticationProvider())
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .authorizeHttpRequests()
                .requestMatchers(HttpMethod.OPTIONS).permitAll()
                .requestMatchers("/**").permitAll()
                .requestMatchers("/auth/login").permitAll()
                .requestMatchers("/auth/logout").permitAll()
                .requestMatchers("/swagger-ui.html").permitAll()
                .requestMatchers("/doc.html").permitAll()
                .requestMatchers("/webjars/springfox-swagger-ui/**").permitAll()
                .requestMatchers("/swagger-resources").permitAll()
                .requestMatchers("/v3/api-docs/**").permitAll()
                .requestMatchers("/favicon.ico").permitAll()
                .requestMatchers("/error").permitAll()
                .anyRequest()
                .authenticated();
        return http.build();
    }
}

5.返回类型 ResponseEntity


import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.util.HashMap;


/**
 * @author zy
 */
@Schema(description = "返回响应数据")
@Data
public class ResponseEntity {
    @Schema(description = "编码")
    private int code = 200;

    @Schema(description = "基本信息")
    private String message = "成功";

    @Schema(description = "错误信息")
    private String errorMsg = "";

    @Schema(description = "返回对象")
    private T data;

    /**
     * 成功状态码
     */
    public static final Integer SUCCESS = 200;

    /**
     * 失败状态码
     */
    public static final Integer ERROR = 500;


    private static HashMap ERROR_CODE = new HashMap() {
        {
            put(100, "暂无数据");
            put(200, "成功");
            put(300, "失败");
            put(500, "失败状态码");
            put(10000, "通用错误");
            ///用户类
            put(10001, "用户名或密码错误");
            put(10002, "登录状态已过期");
            put(10003, "注册用户已存在");
            put(10004, "账号已被锁定,请在一小时后重试");
            put(10005, "旧密码错误");
            put(10006, "用户名已存在");
            put(10007, "ip没有权限");
            put(10008, "token无效");
            put(10009, "token失效");

            ///操作权限类
            put(20001, "无操作权限");
            ///参数类
            put(30001, "非法参数");
            put(30002, "缺少必要参数");
            数据操作类
            put(40001, "添加数据失败");
            put(40002, "更新数据失败");
            put(40003, "删除数据失败");
            put(40004, "添加数据失败,对象已经存在,建议修改或者删除");
            put(50001, "不存在的对象");
            put(99999, "无任何资源权限");

            put(990000, "系统错误");
        }
    };


    public ResponseEntity() {
    }

    public ResponseEntity(T date) {
        this.data = date;
    }

    public int getCode() {
        return code;
    }

    public ResponseEntity setCode(int code) {
        this.code = code;
        if (ERROR_CODE.containsKey(code)) {
            setMessage(ERROR_CODE.get(code));
        }
        return this;
    }


    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public ResponseEntity setData(T data) {
        this.data = data;
        return this;
    }

    public static  ResponseEntity def(Class clazz) {
        return new ResponseEntity<>();
    }

    public ResponseEntity ok() {
        setCode(200);
        return this;
    }

    public ResponseEntity error(int code) {
        setCode(code);
        return this;
    }

    public ResponseEntity message(String message) {
        setMessage(message);
        return this;
    }

    public ResponseEntity data(T data) {
        setData(data);
        return this;
    }

    public ResponseEntity back(int code, String message, T data) {
        setCode(code);
        setMessage(message);
        setData(data);
        return this;
    }

    public static  Boolean isError(ResponseEntity r) {
        return !isSuccess(r);
    }

    public static  Boolean isSuccess(ResponseEntity r) {
        return ResponseEntity.SUCCESS == r.getCode();
    }
}

6.Claims


import lombok.Data;

import java.util.List;

/**
 * jwt实体数据
 */
@Data
public class Claims {
    /**
     * 主题
     */
    private String sub;

    /**
     * 签发时间
     */
    private Long iat;

    /**
     * 过期时间
     */
    private Long exp;

    /**
     * JWT ID
     */
    private String jti;

    /**
     * 用户id
     */
    private String userId;

    /**
     * 用户名
     */
    private String username;

    /**
     * 用户状态(1:正常;0:禁用)
     */
    private String status;

    /**
     * 用户角色
     */
    private List roles;

    /**
     * 权限列表
     */
    private List permissions;

    public Claims(String sub, Long iat, Long exp, String jti, String userId, String username, String status, List roles, List permissions) {
        this.sub = sub;
        this.iat = iat;
        this.exp = exp;
        this.jti = jti;
        this.userId = userId;
        this.username = username;
        this.status = status;
        this.roles = roles;
        this.permissions = permissions;
    }

    public String getSub() {
        return sub;
    }

    public void setSub(String sub) {
        this.sub = sub;
    }

    public Long getIat() {
        return iat;
    }

    public void setIat(Long iat) {
        this.iat = iat;
    }

    public Long getExp() {
        return exp;
    }

    public void setExp(Long exp) {
        this.exp = exp;
    }

    public String getJti() {
        return jti;
    }

    public void setJti(String jti) {
        this.jti = jti;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public List getRoles() {
        return roles;
    }

    public void setRoles(List roles) {
        this.roles = roles;
    }

    public List getPermissions() {
        return permissions;
    }

    public void setPermissions(List permissions) {
        this.permissions = permissions;
    }

    public static ClaimsBuilder builder() {
        return new ClaimsBuilder();
    }

    public static final class ClaimsBuilder {
        //主题
        private String sub;

        //签发时间
        private Long iat;

        //过期时间
        private Long exp;

        //JWT ID
        private String jti;

        //用户id
        private String userId;

        //用户名
        private String username;

        private String status;

        //用户角色
        private List roles;

        private List permissions;

        private ClaimsBuilder() {
        }

        public ClaimsBuilder sub(String sub) {
            this.sub = sub;
            return this;
        }

        public ClaimsBuilder iat(Long iat) {
            this.iat = iat;
            return this;
        }

        public ClaimsBuilder exp(Long exp) {
            this.exp = exp;
            return this;
        }

        public ClaimsBuilder jti(String jti) {
            this.jti = jti;
            return this;
        }

        public ClaimsBuilder userId(String userId) {
            this.userId = userId;
            return this;
        }

        public ClaimsBuilder username(String username) {
            this.username = username;
            return this;
        }

        public ClaimsBuilder status(String status) {
            this.status = status;
            return this;
        }

        public ClaimsBuilder roles(List roles) {
            this.roles = roles;
            return this;
        }

        public ClaimsBuilder permissions(List permissions) {
            this.permissions = permissions;
            return this;
        }

        public Claims build() {
            return new Claims(
                    this.sub,
                    this.iat,
                    this.exp,
                    this.jti,
                    this.userId,
                    this.username,
                    this.status,
                    this.roles,
                    this.permissions);
        }
    }
}

7.LoginUser


import com.zy.entity.SysUser;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {
    private SysUser user;

    @Override
    public Collection getAuthorities() {
        return new ArrayList<>();
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUserName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

8.login登陆

 public ResponseEntity login(SysUser sysUser ) {
        ResponseEntity response = new ResponseEntity<>();
        // 获取用户密码
        String username = sysUser.getUserName();
        String password = sysUser.getPassword();


        // 用户认证
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authenticate = authenticationManagerBuilder.getObject().authenticate(usernamePasswordAuthenticationToken);
        
     
        // 认证成功后,返回用户信息及用户角色信息
        SysUser byNameUser = sysUserMapper.selectByName(username);
       

        LambdaQueryWrapper sysUserRoleLambdaQueryWrapper = new LambdaQueryWrapper<>();
        sysUserRoleLambdaQueryWrapper.eq(SysUserRole::getUserId,userId);
        List sysUserRoles = sysUserRoleMapper.selectList(sysUserRoleLambdaQueryWrapper);

        JSONObject data = new JSONObject();
        data.put("user",byNameUser);

        // jwt实体数据
        Claims claims = Claims.builder()
                .userId(userId)
                .username(byNameUser.getUserName())
                .roles(roles)
                .build();

        JWTUtil jwtUtil = new JWTUtil();
        String token = jwtUtil.createToken(claims);

        data.put("token",token);

        response.setCode(200).setData(data);
        return response;
    } 
  

9.sysUser是用户实体类,相关的代码就不贴了,可以根据自己数据库需求进行编写。

有什么疑问或者教导请指正,谢谢。

你可能感兴趣的:(java,spring,boot,spring)