• SpringSecurity系列——RememberMe实战day7-3(源于官网5.7.2版本)


    RememberMe(记住我)【传统Web】

    相信大家如果是按照文章顺序看下来的话在day3-3的那篇文章中已经看过rememberme的相关介绍了,这里我就不再重复,主要来看实战效果

    了解会话过期配置

    以下的yaml配置设置了会话若一分钟没有任何操作则认为过期,需要重新进行认证,当然默认是30分钟

    server:
      reactive:
        session:
          timeout: 1
    
    • 1
    • 2
    • 3
    • 4

    开启Remember设置

    主要配置

    http.rememberMe()
    
    • 1
    @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            return http
                    .authorizeHttpRequests(auth->auth.anyRequest().authenticated())
                    .httpBasic(Customizer.withDefaults())
                    .csrf().disable()
                    .formLogin(Customizer.withDefaults())
                    .rememberMe()
                    .and().build();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    页面展示

    开启之后我们就会看到出现一个多选框进行remember me的询问了
    在这里插入图片描述
    如果你没有选择记住我的勾选1分钟后(默认30分钟)就会让你重新认证
    勾选登录后查看Cookie可以明显看到信息
    在这里插入图片描述

    通过使用PersistentTokenBasedRememberMeServices加强安全性

    我们可以采用PersistentTokenBasedRememberMeServices的形式修改默认的TokenBasedRememberMeServices,这种实现形式更加安全,在重新获取认证或系统自动重新认证时会更新存储的在Cookie中的remember-me,且解码出来不包含用户名,达到更高的安全性

    @Bean
        public RememberMeServices rememberMeServices(){
            return  new PersistentTokenBasedRememberMeServices(UUID.randomUUID().toString(),userDetailsService,new InMemoryTokenRepositoryImpl())
        }
    
    http.rememberMe()
    	.rememberMeServices(rememberMeServices())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里说一下使用InMemoryTokenRepositoryImpl显然是一种基于内存的实现,如果重启程序我们就会再次进行重新认证,实际上常用的方式是基于数据库的实现JdbcTokenRepositoryImpl
    采用以下方式即可,无需重新注入.rememberMeServices(rememberMeServices())

    http.rememberMe()
    	.tokenRepository(new JdbcTokenRepositoryImpl())
    
    • 1
    • 2

    RememberMe(记住我)【前后端分离

    思想

    在前后端分离的生态下,前端后端并非一个系统,所以相应来说并没有传统Web那么简单,而且也不需要我们配置rememberMe,而是采用令牌和前端的共同设置,但实际上实现的思想是和PersistentTokenBasedRememberMeServices的实现方式相同

    我们简单介绍一下流程

    1. 用户发起登录请求
    2. 用户登陆时传递的表单中有记住我选项标记值
    3. 设置令牌过期时间为7天
    4. 登录用户生成一个token令牌
    5. token存入redis(可选)
    6. token令牌返回前端
    7. 前端存入localStorage中
    8. 后续采用将token作为请求头中的参数进行向后端请求
    9. 后端解析token,通过则允许请求

    知识储备

    技术版本
    Vue3
    typescript-
    axios0.28

    实例

    前端

    在这里插入图片描述

    (axios)request.index.ts

    //导入axios
    import axios, { AxiosInstance,AxiosRequestConfig,AxiosResponse } from 'axios'
    
    export class Request{
      public static axiosInstance:AxiosInstance
    
      public static init(){
        this.axiosInstance = axios.create({
          baseURL:'http://localhost:8050/',
          timeout:1000
        })
        this.initInterceptors()
        return this.axiosInstance
      }
    
      public static initInterceptors(){
        this.axiosInstance.interceptors.request.use(
          (config:AxiosRequestConfig)=>{
            const token = window.localStorage.getItem('token')
            if(token!=null&&config?.headers){
              console.log(token)
              //后续采用将token作为请求头中的参数进行向后端请求
               config.headers.token  = token
            }
            return config
          },
          (error:any)=>{
            console.log(error)
          }
        )
    
        this.axiosInstance.interceptors.response.use(
          (response:AxiosResponse)=>{
            if(response.status===200){
              return response
            }else{
              this.errorHandle(response)
              return null
            }
          },
          (error:any)=>{
            this.errorHandle(error)
          }
        )
      }
    
    
      public static errorHandle(res:any){
        console.log(res)
      }
    }
    
    • 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

    main.ts

    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router'
    import {Request} from '@/request/index'
    
    const app = createApp(App)
    
    app.config.globalProperties.$http = Request.init()
    app.use(router)
    app.mount('#app')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    App.vue

    <template>
      <div id="nav">
        <input type="text" v-model="form.username" name="username"><br>
        <input type="password" v-model="form.password" name="password"><br>
        <button @click="login">登录</button>
        <button @click="getIndex">index</button>
      </div>
      <router-view />
    </template>
    
    <script lang="ts" setup>
    import { reactive } from '@vue/reactivity'
    import { getCurrentInstance } from 'vue'
    
    const { proxy }: any = getCurrentInstance()
    const form = reactive({
      username: '',
      password: '',
      //这里单纯只是我图方便这样写
      rememberMe: true
    })
    
    const login = async () => {
      const { data } = await proxy.$http.post('/login', form)
      //前端存入localStorage中
      window.localStorage.setItem('token', data)
      console.log(data)
    }
    
    const getIndex = async () => {
      const { data } = await proxy.$http.get('/test')
      console.log(data)
    }
    </script>
    
    <style lang="scss">
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
    }
    </style>
    
    
    • 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

    后端

    yaml

    server:
      reactive:
        session:
          timeout: 1
      port: 8050
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/spring_security_test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&allowPublicKeyRetrieval=true
        username: root
        password: $hsduiwzhxajhd-sadha
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    pom.xml

    <dependencies>
            <dependency>
                <groupId>cn.fly</groupId>
                <artifactId>JJWTUtil</artifactId>
                <version>1.1.2</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.79</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.1</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.17</version>
            </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>
        </dependencies>
    
    • 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

    UserRoleCon

    package com.example.rememberme.entity;
    
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName(value = "user_role_con")
    public class UserRoleCon {
        private int id;
        @TableField(value = "userId")
        private long userId;
        @TableField(value = "roleId")
        private long roleId;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    User用户实体类

    需要实现UserDetails接口

    package com.example.rememberme.entity;
    
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    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 java.io.Serializable;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashSet;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName(value = "users")
    public class User implements UserDetails, Serializable {
        @TableId(value = "userId")
        private long userId;
    
        private String username;
        private String password;
        private boolean enabled;
        private boolean expired;
        @TableField(value = "tokenExpired")
        private boolean tokenExpired;
        private boolean locked;
        @TableField(exist = false)
        private ArrayList<Role> roles = new ArrayList<>();
    
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            HashSet<SimpleGrantedAuthority> authorities = new HashSet<>();
            roles.forEach(x->{
                SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(x.getRoleName());
                authorities.add(simpleGrantedAuthority);
            });
            return authorities;
        }
    
        @Override
        public String getPassword() {
            return password;
        }
    
        @Override
        public String getUsername() {
            return username;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return expired;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return locked;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return tokenExpired;
        }
    
        @Override
        public boolean isEnabled() {
            return enabled;
        }
    
        public long getUserId() {
            return userId;
        }
    
        public void setUserId(long userId) {
            this.userId = userId;
        }
    
        public ArrayList<Role> getRoles() {
            return roles;
        }
    
        public void setRoles(ArrayList<Role> roles) {
            this.roles = roles;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    
        public void setExpired(boolean expired) {
            this.expired = expired;
        }
    
        public void setTokenExpired(boolean tokenExpired) {
            this.tokenExpired = tokenExpired;
        }
    
        public void setLocked(boolean locked) {
            this.locked = locked;
        }
    }
    
    
    • 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
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    Role用户角色实体类

    package com.example.rememberme.entity;
    
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName(value = "role")
    public class Role {
        @TableId(value = "roleId")
        private long roleId;
        @TableField(value = "roleName")
        private String roleName;
        @TableField(value = "roleAnnotation")
        private String roleAnnotation;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    UserMapper

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

    UserRoleConMapper

    package com.example.rememberme.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    
    import com.example.rememberme.entity.UserRoleCon;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface UserRoleConMapper extends BaseMapper<UserRoleCon> {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    RoleMapper

    package com.example.rememberme.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    
    import com.example.rememberme.entity.Role;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface RoleMapper extends BaseMapper<Role> {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    LoginController

    package com.example.rememberme.controller;
    
    import com.example.rememberme.param.LoginParam;
    import com.example.rememberme.service.UserService;
    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 {
    
        //注入
        private final UserService userService;
    
        @Autowired
        public LoginController(UserService userService) {
            this.userService = userService;
        }
    
        //登录接口使用remember-me
        @PostMapping("/login")
        public String Login(@RequestBody LoginParam loginParam) {
    
            return userService.login(loginParam);
        }
    }
    
    
    • 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

    TestController

    package com.example.rememberme.controller;
    
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    public class TestController {
    
    
        @PreAuthorize("hasAuthority('ROLE_admin')")
        @GetMapping ("/test")
        public String Test(){
    
            return "test ok";
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    LoginParam前端表单接收类

    package com.example.rememberme.param;
    
    import com.example.rememberme.entity.User;
    import lombok.Data;
    
    import java.io.Serializable;
    
    @Data
    public class LoginParam extends User implements Serializable  {
        private boolean rememberMe;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    JWTUtil

    package com.example.rememberme.util;
    
    import cn.fly.jjwtutil.JJWTUtil;
    import org.springframework.stereotype.Component;
    
    @Component
    public class JWTUtil extends JJWTUtil {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    UserService

    package com.example.rememberme.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.example.rememberme.entity.User;
    import com.example.rememberme.param.LoginParam;
    
    public interface UserService extends IService<User> {
        String login(LoginParam loginParam);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    UserServiceImpl实现UserService

    package com.example.rememberme.service.impl;
    
    import cn.fly.jjwtutil.JWTDefaultExpirationTime;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.example.rememberme.entity.User;
    import com.example.rememberme.mapper.UserMapper;
    import com.example.rememberme.param.LoginParam;
    import com.example.rememberme.service.UserService;
    import com.example.rememberme.util.JWTUtil;
    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.core.Authentication;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;
    import java.util.Objects;
    
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
        private final JWTUtil jwtUtil;
        private final AuthenticationManager authenticationManager;
    
        @Autowired
        public UserServiceImpl(JWTUtil jwtUtil, AuthenticationManager authenticationManager) {
            this.jwtUtil = jwtUtil;
            this.authenticationManager = authenticationManager;
        }
    
    
    
    
        //替换原始流程中的UsernamePasswordAuthenticationFilter
        @Override
        public String login(LoginParam loginParam) {
            //无需对方法进行判断,无需进行json转换
            String username = loginParam.getUsername();
            String password = loginParam.getPassword();
            boolean rememberMe = loginParam.isRememberMe();
    
            //传递到UsernamePasswordAuthenticationFilter中使用loadUserByUsername进行认证
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,password);
            //1.获取AuthenticationManager
            Authentication authenticate = authenticationManager.authenticate(authenticationToken);
            if (Objects.isNull(authenticate)){
                throw new UsernameNotFoundException("用户名或密码错误");
            }
            //认证成功构建token令牌
    
            //判断remember
            if(!rememberMe){
                //未使用则不采用默认构建
                System.out.println("remember me not use!");
                //这里是设置了一天的登录有效凭证,当然我们也会在退出的时候进行清除,后续JJWTUtil中我设置临时凭证的颁布
                //获取让前端颁布sessionStorage的会话存储作为实现
                jwtUtil.initExpirationTime(JWTDefaultExpirationTime.DAY);
    
            }else{
                jwtUtil.defaultBuilder(jwtUtil);
            }
            Claims claims = Jwts.claims();
            User principal = (User) authenticate.getPrincipal();
            claims.put("userId",principal.getUserId());
            String token = jwtUtil.createToken(claims);
    
            return 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
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    DefineUserDetailsServiceImpl实现UserDetailsService

    对用户的登录信息进行获取校验登录

    package com.example.rememberme.service.impl;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.example.rememberme.entity.Role;
    import com.example.rememberme.entity.User;
    import com.example.rememberme.entity.UserRoleCon;
    import com.example.rememberme.mapper.RoleMapper;
    import com.example.rememberme.mapper.UserMapper;
    import com.example.rememberme.mapper.UserRoleConMapper;
    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.ArrayList;
    import java.util.List;
    import java.util.Objects;
    
    @Service
    public class DefineUserDetailsServiceImpl implements UserDetailsService {
        //注入Mapper
        private final UserMapper userMapper;
        private final RoleMapper roleMapper;
        private final UserRoleConMapper userRoleConMapper;
    
        @Autowired
        public DefineUserDetailsServiceImpl(UserMapper userMapper, RoleMapper roleMapper, UserRoleConMapper userRoleConMapper) {
            this.userMapper = userMapper;
            this.roleMapper = roleMapper;
            this.userRoleConMapper = userRoleConMapper;
        }
    
        //认证自动调用本方法进行加载目标用户
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
           //通过username进行查询获取用户信息
    
            User user = this.getUserByUserName(username);
            if (Objects.isNull(user)){
                throw new UsernameNotFoundException("用户名无法获取");
            }
            long userId = user.getUserId();
            //主要获取权限信息
            ArrayList<Role> roleList = this.getAuthoritiesByUserId(userId);
            //封装到GrantedAuthority
            user.setRoles(roleList);
            return user;
        }
    
        private ArrayList<Role> getAuthoritiesByUserId(long userId) {
            LambdaQueryWrapper<UserRoleCon> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserRoleCon::getUserId,userId).select(UserRoleCon::getRoleId);
            List<UserRoleCon> userRoleCons = userRoleConMapper.selectList(queryWrapper);
            ArrayList<Role> roles = new ArrayList<>();
            for (UserRoleCon userRoleCon : userRoleCons) {
                LambdaQueryWrapper<Role> queryWrapper1 = new LambdaQueryWrapper<>();
                queryWrapper1.eq(Role::getRoleId,userRoleCon.getRoleId()).last("limit 1");
                Role role = roleMapper.selectOne(queryWrapper1);
                roles.add(role);
            }
            return roles;
        }
    
        private User getUserByUserName(String username){
            System.out.println("user-"+username);
            LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
            userLambdaQueryWrapper.eq(User::getUsername,username).last("limit 1");
            return  userMapper.selectOne(userLambdaQueryWrapper);
        }
    }
    
    
    • 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
    • 71
    • 72
    • 73

    AuthenicationTokenFilter认证拦截器

    在UsernamePasswordAuthenticationFilter前抢先采用token的解析等方式提供一个SecurityContextHolder,以表示用户已认证

    package com.example.rememberme.filter;
    
    import com.alibaba.fastjson.JSON;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.example.rememberme.entity.User;
    import com.example.rememberme.mapper.UserMapper;
    import com.example.rememberme.util.JWTUtil;
    import io.jsonwebtoken.Claims;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    import static net.sf.jsqlparser.util.validation.metadata.NamedObject.user;
    
    
    //认证判断过滤器
    @Component
    public class AuthenicationTokenFilter extends OncePerRequestFilter {
    
        private final JWTUtil jwtUtil;
        private UserMapper userMapper;
        @Autowired
        public AuthenicationTokenFilter(JWTUtil jwtUtil, UserMapper userMapper) {
            this.jwtUtil = jwtUtil;
            this.userMapper = userMapper;
        }
    
    
    
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            //获取token
            String token = request.getHeader("token");
            //token为空直接放行
            if (!StringUtils.hasText(token)){
                filterChain.doFilter(request,response);
                return;
            }
            //不为空进行解析
            Claims tokenPayLoad = jwtUtil.getTokenPayLoad(token);
            String userId = String.valueOf( tokenPayLoad.get("userId"));
            System.out.println(userId);
    
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getUserId,userId).last("limit 1");
            User user = userMapper.selectOne(queryWrapper);
    
            System.out.println(user);
    
    //        存入SecurityContextHolder
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            //放行
            filterChain.doFilter(request,response);
    
        }
    }
    
    
    • 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

    AuthenticationNotFoundHandler认证失败处理

    实现AccessDeniedHandler

    package com.example.rememberme.handler;
    
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.web.access.AccessDeniedHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.HashMap;
    
    @Component
    public class AuthenticationNotFoundHandler implements AccessDeniedHandler {
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
            String message = accessDeniedException.getMessage();
            HashMap<String, String> data = new HashMap<>();
            data.put("code", String.valueOf(403));
            data.put("exceptionMsg",message);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(data);
            response.getWriter().flush();
            response.getWriter().close();
        }
    }
    
    
    • 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

    SpringSecurityConfig配置

    package com.example.rememberme.config;
    
    import com.example.rememberme.filter.AuthenicationTokenFilter;
    import com.example.rememberme.handler.AuthenticationNotFoundHandler;
    import com.example.rememberme.service.UserService;
    import com.example.rememberme.service.impl.DefineUserDetailsServiceImpl;
    import com.example.rememberme.service.impl.UserServiceImpl;
    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.method.configuration.EnableMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.RememberMeServices;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.rememberme.*;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.CorsConfigurationSource;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    
    import javax.sql.DataSource;
    import java.util.Arrays;
    import java.util.UUID;
    
    @EnableWebSecurity
    @Configuration
    @EnableMethodSecurity
    public class SpringSecurityConfig {
    
        private final AuthenticationNotFoundHandler authenticationNotFoundHandler;
        private final DefineUserDetailsServiceImpl defineUserDetailsService;
        private final AuthenicationTokenFilter authenicationTokenFilter;
    
        @Autowired
        public SpringSecurityConfig(DefineUserDetailsServiceImpl defineUserDetailsService, AuthenicationTokenFilter authenicationTokenFilter,AuthenticationNotFoundHandler authenticationNotFoundHandler) {
            this.defineUserDetailsService = defineUserDetailsService;
            this.authenicationTokenFilter = authenicationTokenFilter;
            this.authenticationNotFoundHandler = authenticationNotFoundHandler;
        }
    
    
    
    
    
        //跨域处理
        @Bean
        public CorsConfigurationSource corsConfigurationSource() {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
            corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
            corsConfiguration.setMaxAge(3600L);
            corsConfiguration.setAllowedMethods(Arrays.asList("*"));
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", corsConfiguration);
            return source;
        }
    
        //获取AuthenticationManager(认证管理器),登录时认证使用
        @Bean
        public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
            return authenticationConfiguration.getAuthenticationManager();
        }
    
    
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            return http
                    .authorizeRequests(auth -> auth
                            .mvcMatchers("/login").anonymous()
                            .anyRequest().authenticated())
                    .httpBasic(Customizer.withDefaults())
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .csrf().disable()
                    .userDetailsService(defineUserDetailsService)
                    .addFilterBefore(authenicationTokenFilter,UsernamePasswordAuthenticationFilter.class)
                    .exceptionHandling()
                    .accessDeniedHandler(authenticationNotFoundHandler)
                    .and()
                    .cors()
                    .configurationSource(corsConfigurationSource())
                    .and()
                    .build();
        }
    
    
    }
    
    
    • 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
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    测试

    前端信息打印
    在这里插入图片描述
    在这里插入图片描述
    后端信息打印

    在这里插入图片描述
    默认token7天后过期,在7天中免登录

  • 相关阅读:
    网络代理技术:保障隐私与增强安全
    【Ubuntu20.04安装java-8-openjdk】
    安装rocketmq-dashboard1.0.1
    Vue学习笔记(二)
    距离的度量
    Taurus: 面向机器学习的数据面架构
    在 Elasticsearch 中实现自动完成功能 1:Prefix queries
    03-安装docker及使用docker安装其他软件(手动挂载数据卷)
    聚观早报 | 达达集团正式回归京东;每日优鲜便利购业务被收购
    cpp primer plus笔记012-STL模板库
  • 原文地址:https://blog.csdn.net/qq_51553982/article/details/126047485