赞
踩
集各位大佬的精华,弄出来的一套基于jdk17,Springboot3.0,SpringSecurity6.0,jwt的登陆及接口拦截体系。
1.先贴依赖:
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>3.0.0</version>
- <relativePath/>
- </parent>
-
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-jdbc</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-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-test</artifactId>
- <scope>test</scope>
- </dependency>
-
- <!-- jwt -->
- <dependency>
- <groupId>com.nimbusds</groupId>
- <artifactId>nimbus-jose-jwt</artifactId>
- <version>9.31</version>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <optional>true</optional>
- </dependency>
-
-
- <!-- mybatis-plus -->
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>3.5.3.1</version>
- </dependency>
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-generator</artifactId>
- <version>3.5.3.1</version>
- <exclusions>
- <exclusion>
- <artifactId>mybatis</artifactId>
- <groupId>org.mybatis</groupId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.alibaba.fastjson2</groupId>
- <artifactId>fastjson2</artifactId>
- <version>2.0.26</version>
- </dependency>
-
-

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<T> {
- @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<Integer, String> ERROR_CODE = new HashMap<Integer, String>() {
- {
- 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 <T> ResponseEntity<T> def(Class<T> clazz) {
- return new ResponseEntity<>();
- }
-
- public ResponseEntity<T> ok() {
- setCode(200);
- return this;
- }
-
- public ResponseEntity<T> error(int code) {
- setCode(code);
- return this;
- }
-
- public ResponseEntity<T> message(String message) {
- setMessage(message);
- return this;
- }
-
- public ResponseEntity<T> data(T data) {
- setData(data);
- return this;
- }
-
- public ResponseEntity<T> back(int code, String message, T data) {
- setCode(code);
- setMessage(message);
- setData(data);
- return this;
- }
-
- public static <T> Boolean isError(ResponseEntity<T> r) {
- return !isSuccess(r);
- }
-
- public static <T> Boolean isSuccess(ResponseEntity<T> 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<String> roles;
-
- /**
- * 权限列表
- */
- private List<String> permissions;
-
- public Claims(String sub, Long iat, Long exp, String jti, String userId, String username, String status, List<String> roles, List<String> 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<String> getRoles() {
- return roles;
- }
-
- public void setRoles(List<String> roles) {
- this.roles = roles;
- }
-
- public List<String> getPermissions() {
- return permissions;
- }
-
- public void setPermissions(List<String> 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<String> roles;
-
- private List<String> 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<String> roles) {
- this.roles = roles;
- return this;
- }
-
- public ClaimsBuilder permissions(List<String> 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<? extends GrantedAuthority> 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<Object> 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<SysUserRole> sysUserRoleLambdaQueryWrapper = new LambdaQueryWrapper<>();
- sysUserRoleLambdaQueryWrapper.eq(SysUserRole::getUserId,userId);
- List<SysUserRole> 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是用户实体类,相关的代码就不贴了,可以根据自己数据库需求进行编写。
有什么疑问或者教导请指正,谢谢。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。