前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringSecurity集成JWT实现后端认证授权保姆级教程-认证配置篇

SpringSecurity集成JWT实现后端认证授权保姆级教程-认证配置篇

作者头像
知识浅谈
发布2024-01-11 10:32:22
2190
发布2024-01-11 10:32:22
举报
文章被收录于专栏:分享学习分享学习

🍁 作者:知识浅谈,CSDN签约讲师,CSDN博客专家,华为云云享专家,阿里云专家博主 📌 擅长领域:全栈工程师、爬虫、ACM算法

视频教程: 上一篇:SpringSecurity集成JWT实现后端认证授权保姆级教程-工具类准备篇 下一篇:SpringSecurity集成JWT实现后端认证授权保姆级教程-授权配置篇 🤞上边的各种配置都完成之后,本节开始进行SpringSecurity的认证🤞

🎈用户类继承UserDetails

为什么要继承这个UserDetails类呢,因为后续SpringSecurity中进行认证授权都是通过实现UserDetail接口和UserDetailsService

针对数据准备篇的CustUser实体类进行更改,实现UserDetails 并实现其对应的方法

代码语言:javascript
复制
@TableName(value ="cust_user")
@Data
public class CustUser implements Serializable, UserDetails {  //这里新增实现UserDetails 
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String nickname;
    private Integer enable;
    private String password;
//----------------------------------------------------以下为新增的部分---------------------------------
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
    //------------------------------------------------------------------------------------------------
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

🎈用户Service实现UserDetailService

实现UserDetailService并实现其对应的loadUserByUsername

针对之前的数据准备篇的CustUserService接口 进行更改

代码语言:javascript
复制
public interface CustUserService extends IService<CustUser>, UserDetailsService {  //这个地方新增继承这个UserDetailsService接口 
}

针对之前的数据准备篇的CustUserServiceImpl实现类 进行更改

代码语言:javascript
复制
@Service
public class CustUserServiceImpl extends ServiceImpl<CustUserMapper, CustUser> implements CustUserService{ //这个地方新增实现CustUserService接口
//-----------------------------------------------------以下为新增的内容------------------------------------------
    @Autowired
    private CustUserMapper custUserMapper;

    @Autowired
    private SysMenuMapper menuMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LambdaQueryWrapper<CustUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CustUser::getUsername, username);
        CustUser user = custUserMapper.selectOne(queryWrapper);
        if (user == null) {
            log.error("用户名不存在");
            throw new UsernameNotFoundException("用户名不存在");
        }else {
            return user;
        }
    }
//----------------------------------------------------------------------------
}

上边的这个主要是实现loadUserByUsername方法在SpringSecurity认证的过程中使用的到。

🎈SecurityConfig配置类重要

在config文件夹下新增Security配置类,这类用于指定拦截路径、设置登录退出接口,异常处理等,直接上代码

代码语言:javascript
复制
package com.example.demo.config;

import com.example.demo.common.Vo.Result;
import com.example.demo.service.CustUserService;
import com.example.demo.utils.JwtUtil;
import com.google.gson.Gson;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.configuration.AuthenticationConfiguration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Slf4j
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法安全权限校验
public class SecurityConfiguration {

    private final JwtUtil jwtUtil; // 注入JwtUtil
    @Autowired
    private CustUserService custUserService;
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
         return new BCryptPasswordEncoder();
    }

    /**
     * 获取AuthenticationManager 登录验证的时候使用
     * @param authenticationConfiguration
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         http.authorizeHttpRequests(
                (authz)->authz
                        .antMatchers("/user/login").permitAll() // 允许匿名用户访问login用于登录
                        .antMatchers("/user/logout").permitAll() // 允许匿名用户访问logout用于登出
                        .antMatchers("/doc.html", "/webjars/**", "/v2/api-docs", "/swagger-resources/**").permitAll() // 允许匿名用户访问swagger
                        .antMatchers("/test").hasAuthority("test") // 拥有test权限的用户才能访问test接口
                        .anyRequest().authenticated()) // 其他请求必须经过身份验证
                .exceptionHandling(conf->conf // 异常处理
                        .authenticationEntryPoint((req, res, authException) -> { //认证异常
                            log.info("认证异常");
                            res.setContentType( "application/json;charset=utf-8" );
                            res.getWriter().write(new Gson().toJson(Result.error(401, authException.getMessage())));
                        })
                        .accessDeniedHandler((req, res, authException) -> { //权限异常
                            log.info("权限异常");
                            res.setContentType("application/json;charset=utf-8");
                            res.getWriter().write(new Gson().toJson(Result.error(403, authException.getMessage())));
                        })
                )
                .csrf(AbstractHttpConfigurer::disable) // 禁用csrf
                .sessionManagement(conf->conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 禁用session
//                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)//指定过滤器
                .cors();
        return http.build();
    }
}

🎈编写登录成功后返回的带有token的实体类

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
package com.example.demo.Vo;


import com.example.demo.domain.CustUser;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AuthUserVo extends CustUser {
    private String token;
}

🎈编写用户登录退出接口

现在我们要实现的是编写登录接口实现用户登录返回token,上边的配置类放行了/user/login 用作登录,本次是编写后端登自定义登录模块,所以不使用SpringSecurity自带的表单登录。

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
package com.example.demo.controller;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.demo.Vo.AuthUserVo;
import com.example.demo.common.Vo.Result;
import com.example.demo.domain.CustUser;
import com.example.demo.utils.JwtUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;
import static com.example.demo.common.constants.OtherConstants.AUTH_TOKEN;
import static com.example.demo.common.constants.OtherConstants.USER_PREFIX;


@Api(tags = "用户模块")
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private AuthenticationManager authenticateManager;
    @Autowired
    private JwtUtil jwtUtil;
    @ApiOperation(value = "用户登录")
    @PostMapping("/login")
    public Result login(@RequestBody CustUser user) {
        Result result = new Result();
        try {
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
            Authentication authenticate = authenticateManager.authenticate(usernamePasswordAuthenticationToken);
            if (authenticate == null) {
                return Result.error(401, "登录校验失败");
            } else {
                user = (CustUser) authenticate.getPrincipal();
                String token = jwtUtil.createJwt(user);//创建token
                AuthUserVo authUserVo = new AuthUserVo();
                BeanUtils.copyProperties(user, authUserVo);
                authUserVo.setToken(token);
    //            将token放在redis中
                redisTemplate.opsForValue().set(USER_PREFIX + String.valueOf(user.getId()),user,30, TimeUnit.MINUTES);
                return Result.success(authUserVo);
            }
        } catch (Exception e) {
            return result.error500("登陆失败");
        }
    }


    @ApiOperation(value = "用户退出")
    @GetMapping("/logout")
    public Result logout(HttpServletRequest req) {
        String token = req.getHeader(AUTH_TOKEN);
        if (token == null || "".equals(token)) {
            return Result.error(401, "token为空");
        } else {
            DecodedJWT decodedJWT = jwtUtil.resolveJwt(token);
            CustUser user = jwtUtil.toUser(decodedJWT);
            redisTemplate.delete(USER_PREFIX + String.valueOf(user.getId()));
            return Result.success("退出成功");
        }
    }
}

🎈登录接口测试

因为我们配置了swagger,所以我们直接在swagger测试登录,最后返回了token swagger文档默认地址(上边的配置文件已经放行文档地址):http://localhost:8080/doc.html

在这里插入图片描述
在这里插入图片描述

到这认证就已经好了,接下来就要进行授权了。

🍚总结

大功告成,撒花致谢🎆🎇🌟,关注我不迷路,带你起飞带你富。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🎈用户类继承UserDetails
  • 🎈用户Service实现UserDetailService
  • 🎈SecurityConfig配置类重要
  • 🎈编写登录成功后返回的带有token的实体类
  • 🎈编写用户登录退出接口
  • 🎈登录接口测试
  • 🍚总结
相关产品与服务
多因子身份认证
多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档