美文网首页
SpringBoot+Vue豆宝社区前后端分离项目手把手实战系列

SpringBoot+Vue豆宝社区前后端分离项目手把手实战系列

作者: __豆约翰__ | 来源:发表于2021-02-24 12:22 被阅读0次

    本节代码开源地址

    代码地址

    用户登录后端(JWT)

    0.JwtUtil

    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.util.*;
    
    public class JwtUtil {
        private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
        public static final long EXPIRATION_TIME = 3600_000_000L; // 1000 hour
        public static final String SECRET = "ThisIsASecret";//please change to your own encryption secret.
        public static final String TOKEN_PREFIX = "Bearer ";
        public static final String HEADER_STRING = "Authorization";
        public static final String USER_NAME = "userName";
    
        public static String generateToken(String userId) {
            HashMap<String, Object> map = new HashMap<>();
            //you can put any data in the map
            map.put(USER_NAME, userId);
            String jwt = Jwts.builder()
                    .setClaims(map)
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                    .signWith(SignatureAlgorithm.HS512, SECRET)
                    .compact();
            return jwt; //jwt前面一般都会加Bearer
        }
    
        public static HttpServletRequest validateTokenAndAddUserIdToHeader(HttpServletRequest request) {
            String token = request.getHeader(HEADER_STRING);
            if (token != null) {
                // parse the token.
                try {
                    Map<String, Object> body = Jwts.parser()
                            .setSigningKey(SECRET)
                            .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                            .getBody();
                    return new CustomHttpServletRequest(request, body);
                } catch (Exception e) {
                    logger.info(e.getMessage());
                    throw new TokenValidationException(e.getMessage());
                }
            } else {
                throw new TokenValidationException("Missing token");
            }
        }
    
        public static class CustomHttpServletRequest extends HttpServletRequestWrapper {
            private Map<String, String> claims;
    
            public CustomHttpServletRequest(HttpServletRequest request, Map<String, ?> claims) {
                super(request);
                this.claims = new HashMap<>();
                claims.forEach((k, v) -> this.claims.put(k, String.valueOf(v)));
            }
    
            @Override
            public Enumeration<String> getHeaders(String name) {
                if (claims != null && claims.containsKey(name)) {
                    return Collections.enumeration(Arrays.asList(claims.get(name)));
                }
                return super.getHeaders(name);
            }
    
        }
    
        static class TokenValidationException extends RuntimeException {
            public TokenValidationException(String msg) {
                super(msg);
            }
        }
    }
    

    1.dto

    @Data
    public class LoginDTO {
    
        @NotBlank(message = "用户名不能为空")
        @Size(min = 2, max = 15, message = "登录用户名长度在2-15")
        private String username;
    
        @NotBlank(message = "密码不能为空")
        @Size(min = 6, max = 20, message = "登录密码长度在6-20")
        private String password;
    
        private Boolean rememberMe;
    }
    

    2.UmsUserController

    @PostMapping("/login")
    public ApiResult login(@Valid @RequestBody LoginDTO loginDTO) {
        Map<String, String> map = umsUserService.login(loginDTO);
        return ApiResult.success(map, "登录成功");
    }
    

    3.UmsUserService

    /**
     * 登录
     *
     * @param loginDTO
     * @return
     */
    public Map<String, String> login(LoginDTO loginDTO) {
        // 邮箱或用户名是否存在
        String loginUserName = loginDTO.getUsername();
        UmsUser umsUser = this.getOne(new LambdaQueryWrapper<UmsUser>()
                .eq(UmsUser::getUsername, loginUserName)
                .or()
                .eq(UmsUser::getEmail, loginUserName));
        if (ObjectUtils.isEmpty(umsUser)) {
            ApiAsserts.fail("用户名或邮箱不存在");
        }
        // 校验密码
        if (!MD5Utils.getPwd(loginDTO.getPassword()).equals(umsUser.getPassword())) {
            ApiAsserts.fail("密码错误,请重新输入");
        }
        // 生成 token
        String token = JwtUtil.generateToken(loginUserName);
        
        HashMap<String, String> map = new HashMap<>(16);
        map.put("token",token);
        return map;
    }
    

    用户登录前端

    1.安装js-cookie

    存放浏览器的Cookies

    yarn add js-cookie
    

    2.src/util创建auth.js

    import Cookies from 'js-cookie'
    
    // 存放token
    const uToken = 'u_token'
    // 存放白天还是黑夜模式
    const darkMode = 'dark_mode';
    
    // 获取Token
    export function getToken() {
        return Cookies.get(uToken);
    }
    
    // 设置Token,1天,与后端同步
    export function setToken(token) {
        return Cookies.set(uToken, token, {expires: 1})
    }
    
    // 删除Token
    export function removeToken() {
        return Cookies.remove(uToken)
    }
    
    export function removeAll() {
        return Cookies.Cookies.removeAll()
    }
    
    export function setDarkMode(mode) {
        return Cookies.set(darkMode, mode, {expires: 365})
    }
    
    export function getDarkMode() {
        return !(undefined === Cookies.get(darkMode) || 'false' === Cookies.get(darkMode));
    }
    

    3.登录路由

    src/router/index.js添加路由

    ,{
        path: '/login',
        name: 'login',
        component: () => import('@/views/auth/login'),
        meta: {title: '登录'}
      }
    

    4./views/auth创建login.vue

    <template>
      <div class="columns py-6">
        <div class="column is-half is-offset-one-quarter">
          <el-card shadow="never">
            <div slot="header" class="has-text-centered has-text-weight-bold">
              用户登录
            </div>
            <div>
              <el-form
                v-loading="loading"
                :model="ruleForm"
                status-icon
                :rules="rules"
                ref="ruleForm"
                label-width="100px"
                class="demo-ruleForm"
              >
                <el-form-item label="账号" prop="name">
                  <el-input v-model="ruleForm.name"></el-input>
                </el-form-item>
    
                <el-form-item label="密码" prop="pass">
                  <el-input
                    type="password"
                    v-model="ruleForm.pass"
                    autocomplete="off"
                  ></el-input>
                </el-form-item>
    
                <el-form-item label="记住" prop="delivery">
                  <el-switch v-model="ruleForm.rememberMe"></el-switch>
                </el-form-item>
    
                <el-form-item>
                  <el-button type="primary" @click="submitForm('ruleForm')"
                    >提交</el-button
                  >
                  <el-button @click="resetForm('ruleForm')">重置</el-button>
                </el-form-item>
              </el-form>
            </div>
          </el-card>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "Login",
      data() {
        return {
          redirect: undefined,
          loading: false,
          ruleForm: {
            name: "",
            pass: "",
            rememberMe: true,
          },
          rules: {
            name: [
              { required: true, message: "请输入账号", trigger: "blur" },
              {
                min: 2,
                max: 15,
                message: "长度在 2 到 15 个字符",
                trigger: "blur",
              },
            ],
            pass: [
              { required: true, message: "请输入密码", trigger: "blur" },
              {
                min: 6,
                max: 20,
                message: "长度在 6 到 20 个字符",
                trigger: "blur",
              },
            ],
          },
        };
      },
      methods: {
        submitForm(formName) {
          this.$refs[formName].validate((valid) => {
            if (valid) {
              this.loading = true;
              this.$store
                .dispatch("user/login", this.ruleForm)
                .then(() => {
                  this.$message({
                    message: "恭喜你,登录成功",
                    type: "success",
                    duration: 2000,
                  });
                  setTimeout(() => {
                    this.loading = false;
                    this.$router.push({ path: this.redirect || "/" });
                  }, 0.1 * 1000);
                })
                .catch(() => {
                  this.loading = false;
                });
            } else {
              return false;
            }
          });
        },
        resetForm(formName) {
          this.$refs[formName].resetFields();
        },
      },
    };
    </script>
    
    <style scoped>
    </style>
    

    5.API请求地址

    // 登录
    export function login(data) {
        return request({
          url: '/auth/user/login',
          method: 'post',
          data
        })
      }
    

    6.src/store创建modules/user.js

    import { getUserInfo, login, logout } from "@/api/auth/auth";
    import { getToken, setToken, removeToken } from "@/utils/auth";
    
    const state = {
      token: getToken(), // token
      user: "", // 用户对象
    };
    
    const mutations = {
      SET_TOKEN_STATE: (state, token) => {
        state.token = token;
      },
      SET_USER_STATE: (state, user) => {
        state.user = user;
      },
    };
    
    const actions = {
      // 用户登录
      login({ commit }, userInfo) {
        console.log(userInfo);
        const { name, pass, rememberMe } = userInfo;
        return new Promise((resolve, reject) => {
          login({ username: name.trim(), password: pass, rememberMe: rememberMe })
            .then((response) => {
              const { data } = response;
              commit("SET_TOKEN_STATE", data.token);
              setToken(data.token);
              resolve();
            })
            .catch((error) => {
              reject(error);
            });
        });
      },
    
      // 获取用户信息
      getInfo({ commit, state }) {
        return new Promise((resolve, reject) => {
          getUserInfo()
            .then((response) => {
              const { data } = response;
              if (!data) {
                commit("SET_TOKEN_STATE", "");
                commit("SET_USER_STATE", "");
                removeToken();
                resolve();
                reject("Verification failed, please Login again.");
              }
              commit("SET_USER_STATE", data);
              resolve(data);
            })
            .catch((error) => {
              reject(error);
            });
        });
      },
    
      // 注销
      logout({ commit, state }) {
        return new Promise((resolve, reject) => {
          logout(state.token)
            .then((response) => {
              console.log(response);
              commit("SET_TOKEN_STATE", "");
              commit("SET_USER_STATE", "");
              removeToken();
              resolve();
            })
            .catch((error) => {
              reject(error);
            });
        });
      },
    };
    
    export default {
      namespaced: true,
      state,
      mutations,
      actions,
    };
    

    7.src/store的index.js

    index.js的全部内容

    import Vue from 'vue'
    import Vuex from 'vuex'
    import user from './modules/user'
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
        modules: {
            user
        }
    })
    
    export default store
    
    

    8.测试页面

    输入正确的用户名和密码之后,在Cookies中会生成token

    image-20210212121757027

    登录欢迎侧边栏

    1.veiws/card/LoginWelcome.vue

    复制一下内容替换

    <template>
      <el-card class="box-card" shadow="never">
        <div slot="header">
          <span>💐 发帖</span>
        </div>
        <div v-if="token != null && token !== ''" class="has-text-centered">
          <b-button type="is-danger" tag="router-link" :to="{path:'/post/create'}" outlined>✍ 发表想法</b-button>
        </div>
    
        <div v-else class="has-text-centered">
          <b-button type="is-primary" tag="router-link" :to="{path:'/register'}" outlined>马上入驻</b-button>
          <b-button type="is-danger" tag="router-link" :to="{path:'/login'}" outlined class="ml-2"> 社区登入</b-button>
        </div>
      </el-card>
    </template>
    
    <script>
    import { mapGetters } from 'vuex'
    export default {
      name: 'LoginWelcome',
      computed: {
        ...mapGetters([
          'token'
        ])
      }
    }
    </script>
    
    <style scoped>
    </style>
    

    2.src/store/创建getters.js

    const getters = {
        token: state => state.user.token,   // token
        user: state => state.user.user,     // 用户对象
    }
    export default getters
    

    3.修改src/store/index.js

    image-20210212123532258

    4.重启查看页面

    image-20210212124047884

    相关文章

      网友评论

          本文标题:SpringBoot+Vue豆宝社区前后端分离项目手把手实战系列

          本文链接:https://www.haomeiwen.com/subject/vqpvxltx.html