• SpringSecurity系列——基于SpringBoot2.7的登录接口(内有惊喜)day2-1


    SpringSecurity系列——基于SpringBoot2.7的登录接口

    完整登录

    本文所用JWTUtil地址

    这个JWT工具类是我自己写的,具体怎么使用都在README.md中了
    高度封装使用简单
    git@gitee.com:giteeforsyf/jjwtutil.git

    环境

    SpringBoot版本:2.7
    SpringSecurity版本:5.4+
    注:SpringSecurityConfigurationAdapter已过时

    准备工作

    在准备工作中我们需要准备数据库、实体类、配置、工具类等

    目录

    在这里插入图片描述

    数据库

    数据库设计如图:
    在这里插入图片描述

    在这里插入图片描述

    pom.xml

    
            
                cn.fly
                JJWTUtil
                1.1.2
            
            
                mysql
                mysql-connector-java
                8.0.17
            
            
                com.baomidou
                mybatis-plus-boot-starter
                3.5.1
            
            
                org.springframework.boot
                spring-boot-starter-security
            
            
                org.springframework.boot
                spring-boot-starter-web
            
            
                org.springframework.boot
                spring-boot-starter-web-services
            
            
                org.springframework.boot
                spring-boot-starter-data-redis
            
            
                com.alibaba
                fastjson
                1.2.79
            
    
            
                org.springframework.boot
                spring-boot-devtools
                runtime
                true
            
            
                org.springframework.boot
                spring-boot-configuration-processor
                true
            
            
                org.projectlombok
                lombok
                true
            
            
                org.springframework.boot
                spring-boot-starter-test
                test
            
            
                org.springframework.security
                spring-security-test
                test
            
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    实体类User

    package com.example.ss1.entity;
    
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    @TableName("user")
    public class User {
        private Long id;
        private String username;
        private String password;
        private String status;
        private String nickname;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    UserMapper

    package com.example.ss1.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.example.ss1.entity.User;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface UserMapper extends BaseMapper {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    LoginService

    package com.example.ss1.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.example.ss1.entity.User;
    import com.example.ss1.parm.LoginParam;
    import com.example.ss1.util.ResultUtil;
    
    public interface LoginService extends IService {
        ResultUtil login(LoginParam loginParam);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ResultUtil统一封装返回

    package com.example.ss1.util;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class ResultUtil {
        private int code;
        private String Message;
        private Object data;
    
        public static ResultUtil success(int code, String msg, Object data) {
            ResultUtil ResultUtil = new ResultUtil();
            ResultUtil.setCode(200);
            ResultUtil.setMessage(msg);
            ResultUtil.setData(data);
            return ResultUtil;
        }
    
        public static ResultUtil success() {
            return success(200, null, null);
        }
    
        public static ResultUtil success(String msg) {
            return success(200, msg, null);
        }
    
        public static ResultUtil success(String msg, Object data) {
            return success(200, msg, data);
        }
    
        public static ResultUtil fail(int code, String msg, Object data) {
            ResultUtil ResultUtil = new ResultUtil();
            ResultUtil.setCode(code);
            ResultUtil.setMessage(msg);
            ResultUtil.setData(data);
            return ResultUtil;
        }
    
        public static ResultUtil fail(int code) {
            return success(code, null, null);
        }
    
        public static ResultUtil fail(int code, String msg) {
            return success(code, msg, null);
        }
    
        public static ResultUtil fail() {
            return success(404, null, null);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    LoginParam前端表单接收

    package com.example.ss1.parm;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.io.Serializable;
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public class LoginParam implements Serializable {
        private String username;
        private String password;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    JWTUtil

    这里解释一下,由于我直接使用了我自己写的JJWTUtil所以这里只要将该类是设置为配置类继承JJWTUtil即可

    package com.example.ss1.util;
    
    import cn.fly.jjwtutil.JJWTUtil;
    import lombok.Data;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @Data
    public class JWTUtil extends JJWTUtil {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    LoginController

    package com.example.ss1.controller;
    
    import com.example.ss1.parm.LoginParam;
    import com.example.ss1.service.LoginService;
    import com.example.ss1.util.ResultUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class LoginController {
        @Autowired
        private LoginService loginService;
    
        @PostMapping("/user/login")
        public ResultUtil login(@RequestBody LoginParam loginParam){
            return loginService.login(loginParam);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    SpringSecurity配置

    SpringSecurityConfig

    对SpringSecurity的配置类,这里面编译器提示无法自动装配不用管
    在这里插入图片描述

    package com.example.ss1.config;
    
    import com.example.ss1.service.impl.UserDetailsServiceImpl;
    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.Customizer;
    import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.CorsConfigurationSource;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    
    
    @Configuration
    public class SpringSecurityConfig {
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
        
        //获取AuthenticationManager(认证管理器),登录时认证使用
        @Bean
        public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
            return authenticationConfiguration.getAuthenticationManager();
        }
    
        //配置过滤
        @Bean
        SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            return http.csrf().disable()//关闭csrf
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//关闭session
                    .and()
                    .authorizeRequests(auth ->
                            auth.antMatchers("/**").permitAll()
                                    .anyRequest().authenticated()
                    )
                    .userDetailsService(userDetailsService).build();
        }
    
        //配置加密方式
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        //配置跨源访问(CORS)
        @Bean
        CorsConfigurationSource corsConfigurationSource(){
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**",new CorsConfiguration().applyPermitDefaultValues());
            return source;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    UserDetailsServiceImpl

    package com.example.ss1.service.impl;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.example.ss1.entity.User;
    import com.example.ss1.entity.UserLogin;
    import com.example.ss1.mapper.UserMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    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.Service;
    
    import java.util.Objects;
    
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
        @Autowired
        private UserMapper userMapper;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //查询用户信息
            LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getUsername, username).last("limit 1");
            User user = userMapper.selectOne(queryWrapper);
            //异常
            if (Objects.isNull(user)){
                throw new UsernameNotFoundException("用户名未发现");
            }
            //查询对应权限信息
    
            //数据封装为UserDetails返回
            return new UserLogin(user);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    UserLogin(UserDetails的实现类)

    由于我们需要在UserDetailsServiceImpl中将数据进行封装
    所以我们就需要有这样一个类实现UserDetails接口,定义返回的一系列方法

    package com.example.ss1.entity;
    
    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.Collection;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class UserLogin implements UserDetails {
    
        //传入用户对象
        private User user;
    
        /**
         * 判断权限信息
         * @return
         */
        @Override
        public Collection getAuthorities() {
    
            return null;
        }
    
        @Override
        public String getPassword() {
            return user.getPassword();
        }
    
        @Override
        public String getUsername() {
            return user.getUsername();
        }
    
        /**
         * 判断是否未过期
         * @return
         */
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        /**
         * 判断账户是否未锁定
         * @return
         */
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        /**
         * 判断是否可以使用
         * @return
         */
        @Override
        public boolean isEnabled() {
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    LoginServiceImpl

    package com.example.ss1.service.impl;
    
    import cn.fly.jjwtutil.JJWTUtil;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.example.ss1.entity.User;
    import com.example.ss1.entity.UserLogin;
    import com.example.ss1.mapper.UserMapper;
    import com.example.ss1.parm.LoginParam;
    import com.example.ss1.service.LoginService;
    import com.example.ss1.util.JWTUtil;
    import com.example.ss1.util.ResultUtil;
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.authorization.AuthorizationManager;
    import org.springframework.security.core.Authentication;
    import org.springframework.stereotype.Service;
    
    import java.util.Objects;
    
    @Service
    public class LoginServiceImpl extends ServiceImpl implements LoginService{
    
        @Autowired
        private JJWTUtil jjwtUtil;
        @Autowired
        private AuthenticationManager authenticationManager;
    
    
        @Override
        public ResultUtil login(LoginParam loginParam) {
            //进行用户认证。获取AuthenticationManager authenticate
            //获取认证对象
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginParam.getUsername(), loginParam.getPassword());
            //认证
            Authentication authenticate = authenticationManager.authenticate(authenticationToken);
            //认证失败
            if (Objects.isNull(authenticate)){
                throw new RuntimeException("登录失败");
            }
            //认证成功,生成token
            //获取用户信息(getPrincipal())
            UserLogin user = (UserLogin) authenticate.getPrincipal();
            Long id = user.getUser().getId();
            Claims claims = Jwts.claims();
            claims.put("userId",id);
            jjwtUtil.defaultBuilder(jjwtUtil);
            String token = jjwtUtil.createToken(claims);
            System.out.println(token);
            //返回
            return ResultUtil.success("登录成功",token);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    关于为什么获取用户信息使用authenticate.getPrincipal()方法

    以下是调试结果,可以看到返回的用户信息是在Principal里的
    其实在官网里很明确的说了
    在这里插入图片描述

    使用ApiFox测试

    在这里插入图片描述

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    基于UDP协议的聊天室项目
    汽车金融软件解决方案:助力行业适应产业变化完成产业升级
    LeetCode - 112. 路径总和;113. 路径总和 II【进阶】
    第60章 ApplicationPart自动集成整体性和独立性插件项
    计算机毕业设计Java网上投稿管理系统(源码+系统+mysql数据库+Lw文档)
    【脑源成像】术前癫痫的电源成像 评价:现状与未来展望
    JVM 面试速记
    Redis设计与实现(五)| Sentinel哨兵
    腾讯云轻量服务器地域选择方法整理,选择不能修改!
    白嫖一个属于你的私有大模型
  • 原文地址:https://blog.csdn.net/m0_67393157/article/details/126116578