登录综合案例01,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2
登录综合案例,前端使用vue+elementui+axios后端 springboot+mybatis-plus+swagger2前端使用vue+elementui+axios进入可视化界面创建新项目前端使用vue+elementui+axios进入可视化界面创建新项目
登录综合案例01,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2
前端使用vue+elementui+axios
进入可视化界面创建新项目
开始工作
注意:
(1) App.vue中必须要加路由渲染
(2)在index.js中配置login.vue的路径
{
path: '/login',
name: 'Login',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/login.vue')
}
新建login.vue的js
export default 只能导出一个默认模块
<script>
export default {
name: "login"
}
</script>
注意:如果从git上下载项目,就要执行以下命令
设计登录页面
(1)login.vue组件页面
<template>
<!-- 登录的容器-->
<div class="login_container">
登录页面
</div>
</template>
(2) 设置登陆界面的css样式
<style scoped>
.login_container{
background-color: #e2a5b9;
height: 100%;
}
</style>
(3)发现.login_container高度无效。 在assets下创建一个全局的css
body,html,#app{
height: 100%;
padding: 0;
margin: 0;
font-size: 12px;
}
(4)注意:一定要把全局css导入main.js 使用需要的插件,作用:加载公共组件
//导入全局css
import './assets/css/global.css'
(5)登陆框和登陆框的头像设计布局
<template>
<!-- 登录的容器-->
<div class="login_container">
<!-- 登录盒子 -->
<div class="login_box">
<!-- 头像 -->
<div class="avatar_box">
<img src="../assets/dog.png" alt="">
</div>
<!-- 登录表单 -->
</div>
</div>
</template>
(6)css样式
<style scoped>
.login_container{
background-color: #2b5b6b;
height: 100%;
}
.login_box {
width: 450px;
height: 300px;
background: #fff;
border-radius: 3px;
/**绝对定位*/
position: absolute;
/**左偏移*/
left: 50%;
/**上偏移*/
top: 50%;
/**减去容器自身的宽高*/
transform: translate(-50%, -50%);
}
.login_box>.avatar_box{
height: 130px;
width: 130px;
border: 1px solid #eee;
border-radius: 50%;
padding: 5px;
/**阴影*/
box-shadow: 0 0 10px #ddd;
/**绝对定位*/
position: absolute;
left: 50%;
transform: translate(-50%, -50%);
background-color: pink;
}
.login_box>.avatar_box>img {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #eee;
}
</style>
(7)设计表单输入框
<template>
<!-- 登录的容器-->
<div class="login_container">
<!-- 登录盒子 -->
<div class="login_box">
<!-- 头像 -->
<div class="avatar_box">
<img src="../assets/dog.png" alt="">
</div>
<!-- 登录表单 -->
<div style="margin: 20px;"></div>
<el-form class="login_form" >
<el-form-item >
<el-input prefix-icon="el-icon-user" placeholder="请输入用户名" v-model="loginFormData.loginName" ></el-input>
</el-form-item>
<el-form-item >
<el-input prefix-icon="el-icon-lock" placeholder="请输入密码" v-model="loginFormData.password"></el-input>
</el-form-item>
<el-form-item class="btns">
<el-button type="primary" >提交</el-button>
<el-button>重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
(8)表单的css
/*表单的设计*/
.login_form {
position: absolute;
bottom: 0;
width: 100%;
padding: 0 20px;
box-sizing: border-box;
}
.btns {
display: flex;
justify-content: flex-end;
}
登陆时的表单校验
(1)在表单元素上添加 :rules=“myRules”
(2)在data中定义校验规则:
data(){
return {
myRules:{
}
}
}
(3)在表单元素上使用指定的校验规则
注意:如果vue表单输入的有数据,但是提示不能为空,检查以下
第一个地方是el-form标签是否绑定了值,也就是:model=""
第二个地方是el-form标签是否绑定了表单验证规则,也就是:rules=""
第三个地方是el-form-item标签是否有prop,并且prop的值是否和rules的值对应
注意:如果表单输入不上值,一定要把表单中的:model属性改为v-model
表单校验的代码
<!-- 登录表单 -->
<div style="margin: 20px;"></div>
<!-- 添加校验规则第一步添加:rules="loginRules" 表单的校验规则-->
<el-form class="login_form" :model="loginFormData" :rules="LoginRules">
<!-- 第二步加上prop这个属性 表示使用表单中的那个校验规则-->
<el-form-item prop="loginName">
<!-- 第三步注意prop的属性值是否与每个输入框里面的v-model的属性是否对照-->
<el-input prefix-icon="el-icon-user" placeholder="请输入用户名" v-model="loginFormData.loginName" ></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input prefix-icon="el-icon-lock" placeholder="请输入密码" v-model="loginFormData.password"></el-input>
</el-form-item>
<el-form-item class="btns">
<el-button type="primary" >提交</el-button>
<el-button>重置</el-button>
</el-form-item>
</el-form>
校验的代码
<script>
export default {
name: "login",
data(){
return {
loginFormData:{
loginName:"",
password:"",
},
//定义登录的校验规则
LoginRules:{
loginName:[
{ required: true, message: '用户名不能为空', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
password:[
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 6, max: 8, message: '长度在 6到 8 个字符', trigger: 'blur' }
]
}
}
}
}
</script>
重置表单
(1)为表单添加一个属性 ref=“名称”
(2)重置的语句 loginFormRef是指ref的值
this.$refs.loginFormRef.resetFields();
重置的方法:
methods: {
//重置方法
loginReset() {
console.log(this);
this.$refs.loginFormRef.resetFields();
},
}
登陆后台接口
(1)依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zz</groupId>
<artifactId>springboot-vue</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-vue</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis-plus/依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- mybatis-plus-generator代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 模板引擎 依赖,-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<!-- druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<!-- swagger2-->
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
<version>1.9.1.RELEASE</version>
</dependency>
<!-- swagger 好看的ui-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<!-- 引入shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
(2)mp代码生成器
package com.zz.springbootvue01;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
// 获取工程的根目录
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");//
gc.setAuthor("张峥");
gc.setOpen(false);//是否生成代码后打开本地目录
gc.setSwagger2(true); //是否生存实体属性 Swagger2 注解
gc.setServiceName("%sService"); //service命名
gc.setMapperName("%sDao"); //Dao命名
//
mpg.setGlobalConfig(gc);//是否设置全局配置
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/springboot_vue?serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("aaa");//模块名
pc.setParent("com.zz");//设置父包 com.zz.aaa.controller dao service entity
// 设置dao
pc.setMapper("dao");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 velocity
String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 预计目录 mapper/aaa/UserMapper.xml
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
//关闭系统模板引擎
templateConfig.setXml(null);
//放入空的模板引擎替换掉默认的模板引擎
mpg.setTemplate(templateConfig);
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);//是否采用驼峰命名
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//列是否要驼峰命名
strategy.setEntityLombokModel(true);// 是否要lombok
// 是否要前缀
strategy.setTablePrefix("acl_");
strategy.setRestControllerStyle(true);//controller是否使用restful风格
mpg.setStrategy(strategy);
mpg.execute();
}
}
(3)swagger2的配置,注意要更改在哪个包下生产api文档
.apis(RequestHandlerSelectors.basePackage(“com.zz.aaa.controller”))
@Configuration//配置类
public class SwaggerConfig {
// swagger2的是实例对象Docket
@Bean
public Docket getDocket() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("Qy129")
.apiInfo(apiInfo())
.select()
// 设置哪些包下的类生产api文档
.apis(RequestHandlerSelectors.basePackage("com.zz.aaa.controller"))
// 设置哪些请求路径生产接口文档
.paths(PathSelectors.any())
.build();
return docket;
}
private ApiInfo apiInfo() {
Contact DEFAULT_CONTACT = new Contact("张峥", "http://www.jd.com", "2300326070@qq.com");
ApiInfo apiInfo = new ApiInfo("管理系统api接口", "管理系统api接口", "2.0", "http://www.baidu.com",
DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>());
return apiInfo;
}
}
(4)主启动类
package com.zz.springbootvue01;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@EnableSwagger2//开始awagger2注解
@MapperScan("com.zz.aaa.dao")//扫描dao层
public class SpringbootVue01Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootVue01Application.class, args);
}
}
注意:在主实现类上开启swagger2的注解和包扫描的注解
@EnableSwagger2//开启swagger2的注解
@MapperScan(“com.zz.aaa.dao”)//扫描dao层
(5)application.yml的配置信息
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_vue?serverTimezone=Asia/Shanghai
username: root
password: root
server:
port: 8081
logging:
level:
com.zz.aaa.dao: debug
(6)LoginController层登录接口代码
package com.zz.aaa.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zz.aaa.entity.User;
import com.zz.aaa.vo.CommonResult;
import com.zz.aaa.vo.LoginVo;
import com.zz.aaa.service.UserService;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author : 小峥
* @date : 2021/5/2 22:28
* @description:
*/
@RestController
@Api("登录接口API")
@RequestMapping("/aaa/login")
public class LoginController {
@Resource
private UserService userService;
/**
* 登录的接口
* @return
*/
@PostMapping("login")//LoginVo用来接收登陆者的信息
public CommonResult login(LoginVo loginVo) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", loginVo.getLoginName());
wrapper.eq("password", loginVo.getPassword());
User user = userService.getOne(wrapper);
if (user != null) {
return new CommonResult(2000, "登录成功", user);
} else {
return new CommonResult(5000, "登录失败", null);
}
}
}
CommonResult 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("响应的对象")
public class CommonResult {
@ApiModelProperty("响应的状态码")
private Integer code;
@ApiModelProperty("响应的信息")
private String msg;
@ApiModelProperty("响应的数据")
private Object result;
}
接收登陆者的信息LoginVo实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("登录的对象")
public class LoginVo {
@ApiModelProperty(value = "账户")
private String loginName;
@ApiModelProperty(value = "密码")
private String password;
}
前端调用后端接口
(1)在main.js中引入axios并挂载到Vue对象上
import axios from 'axios'
//把axios挂载到vue对象
Vue.prototype.$http=axios;
(2)登录页面ajax请求登录成功跳转home.vue页面,这里一定要先表单验证成功后在进行ajax操作
这里的官网上的foemName是指表单上ref="名称"的值
//登录的ajax
submit() {
var that=this;
this.$refs.loginFormRef.validate((valid) => {
//表单验证成功后
if (valid) {
this.$http.post("http://localhost:8081/aaa/login/login", this.loginFormData).then(function (dataInfo) {
// console.log(dataInfo)
if (dataInfo.data.code === 2000) {
//显示登录成功
that.$message.success(dataInfo.data.msg);
// 并且跳转页面
that.$router.push("/home");
} else {
console.log(dataInfo.data.msg)
that.$message.error(dataInfo.data.msg);
}
})
}
});
}
(3)以下表示出现跨域问题
什么情况下会出现跨域问题?
(1)必须是ajax请求
(2)从一个区域请求另一个区域,协议不同或者ip不同或者端口号不同。
解决:只需要在后台LoginController页面加上注解**@CrossOrigin**
(4)注入home.vue组件
①创建home.vue组件
②在index.js中注册home.vue组件
{
path: '/home',
name: 'Home',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/home.vue')
}
完成在密码框回车即登录
(1)在表单上添加 @submit.native.prevent
(2)在密码框上添加 @keyup.enter.native.prevent=“submit()”
添加回车触发登录的方法 这里的submit()表示登录的方法。
完善登陆功能
(1)登录成功后,前端要保存登录这的信息
保存在sessionStorage.setItem(key,value)
sessionStorage在当前窗口有效
localStorage再新开一个窗口也有效,关闭浏览器无效
(2)登录成功后,后端也会保存登录者的信息
保存在redis中,并且会随机产生一个key,value为用户的信息,并且把key相应给前端。
注意要先把redis的配置添加到application.yml
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///springboot_vue?serverTimezone=Asia/Shanghai
username: root
password: root
redis:
host: 127.0.0.1
port: 6379
server:
port: 8081
logging:
level:
com.zz.dao: debug
添加RedisConfig 配置类,自定义序列化方式
package com.zz.aaa.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
//比如验证码
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
后端LoginController接口方法改动
@PostMapping("login")
private CommonResult login(@ApiParam("登录者信息") @RequestBody LoginVo loginVo) {
return userService.findByNameAndPassword(loginVo);
}
后端UserServiceImpl方法改动
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {
@Resource
private UserDao userDao;
@Resource
private RedisTemplate redisTemplate;
@Override
public CommonResult findByNameAndPassword(LoginVo loginVo) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", loginVo.getLoginName());
wrapper.eq("password", loginVo.getPassword());
User user = userDao.selectOne(wrapper);
if (user!= null) {
// 登录成功就把用户的信息保存到redis
// 随机产生一个key
String key = UUID.randomUUID().toString().replace("-", "");
// 把随机产生的key作为key,用户的信息当做value
redisTemplate.opsForValue().set(key,user,24, TimeUnit.HOURS);
return new CommonResult(2000, "登录成功", key);
} else {
return new CommonResult(5000, "登录失败", null);
}
}
}
前台登录的方法改动
//登录的ajax
submit() {
var that=this;
this.$refs.loginFormRef.validate((valid) => {
//表单验证成功后
if (valid) {
this.$http.post("http://localhost:8081/aaa/login/login", this.loginFormData).then(function (dataInfo) {
if (dataInfo.data.code === 2000) {
//显示登录成功
that.$message.success(dataInfo.data.msg);
// 把用户的key保存起来 sessionStorage localStorage 本地
// console.log(dataInfo.data.result)
sessionStorage.setItem("token",dataInfo.data.result);
// 并且跳转页面
that.$router.push("/home");
} else {
console.log(dataInfo.data.msg)
that.$message.error(dataInfo.data.msg);
}
})
}
});
}
路由守卫(只放行login)
在main.js中添加路由守卫的代码:
//to:路由跳转的路径
//from:从哪跳转来的
//next:执行
router.beforeEach((to,from,next)=>{
var path=to.path;
if (path==="/login"){//如果跳转路径为login则放行
return next();
}
// 判断是否携带token值
var token=sessionStorage.getItem("token");
if (token){
return next();//已经登录则放行
}
return next("/login");
})
设置请求头的值----请求拦截器
在main.js中添加,要放在挂载vue对象的上面
//axios请求拦截器
axios.interceptors.request.use(config=>{
var token = sessionStorage.getItem("token");
if (token){
config.headers.token=token;
}
return config;
})
引入shiro安全框架
(1)引入依赖
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>
(2)引入ShiroConfig配置类,注意:这里还需要放行swagger的相关路径
如果加了@CrossOrigin跨域注解,前台访问还是报错跨域问题,则查看访问的路径是否放行,或是否有权限 等原因都会导致跨域
一定要放行登录的路径退出的路径
package com.zz.aaa.config;
import com.zz.aaa.realm.MyRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.HashMap;
@Configuration
public class ShiroConfig {
@Bean("securityManager")
public DefaultWebSecurityManager securityManager(Realm myRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
return securityManager;
}
// IOC控制反转 DI依赖注入
@Bean(value = "myRealm")
public Realm getRealm(CredentialsMatcher credentialsMatcher) {
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(credentialsMatcher);
return myRealm;
}
@Bean(value = "credentialsMatcher")
public CredentialsMatcher getCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashIterations(1024);
credentialsMatcher.setHashAlgorithmName("MD5");
return credentialsMatcher;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//重点 shiro 不配置登录页面 会默认跳转到login.jsp
HashMap<String, String> map = new HashMap<>();
map.put("/aaa/login/login", "anon");
map.put("/aaa/login/exit", "anon");
// 这里还需要放行swagger的相关路径
map.put("/doc.html","anon"); //anon表示放行
map.put("/webjars/**","anon"); //anon表示放行
map.put("/swagger-resources/**","anon"); //anon表示放行
map.put("/v2/**","anon"); //anon表示放行
map.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
// 注册filter组件
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setName("shiroFilter");
filterRegistrationBean.setFilter(new DelegatingFilterProxy());
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
(3)引入MyRealm
package com.zz.aaa.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zz.aaa.entity.User;
import com.zz.aaa.service.UserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import javax.annotation.Resource;
public class MyRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// User user = (User) principalCollection.getPrimaryPrincipal();
// List<String> permissions = userService.findPermissionByUserid(user.getUserid());
// SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// if (permissions.size()>0){
// info.addStringPermissions(permissions);
// }
// return info;
return null;
}
/**
* 登录认证
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户的用户名
String username = authenticationToken.getPrincipal().toString();
// 根据用户名获取用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
User user = userService.getOne(wrapper);
if (user!=null){
// 获得盐
ByteSource salt = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), salt, this.getName());
return info;
}
return null;
}
}
(4)UserServiceImpl类,service的代码
@Service
@Slf4j//打印日志
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {
@Resource
private UserDao userDao;
@Resource
private RedisTemplate redisTemplate;
@Override
public CommonResult findByNameAndPassword(LoginVo loginVo) {
try {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getLoginName(),loginVo.getPassword());
subject.login(token);
// 获得登录的信息
Object user = subject.getPrincipal();
// 登录成功就把用户的信息保存到redis
// 随机产生一个key
String key = UUID.randomUUID().toString().replace("-", "");
// 把随机产生的key作为key,用户的信息当做value
redisTemplate.opsForValue().set(key,user,24,TimeUnit.HOURS);
return new CommonResult(2000, "登录成功", key);
} catch (AuthenticationException e) {
e.printStackTrace();
return new CommonResult(5000, "登录失败", null);
}
}
}
测试接口的swagger2路径
http://localhost:8081/doc.html
登录成功页面的布局
<template>
<el-container class="home-container">
<el-header>
<div><img src="../assets/dog.png" width="60" alt="">
<span>进入首页</span>
<el-button type="danger" @click="exit">危险按钮</el-button>
</div>
</el-header>
<el-container>
<el-aside width="200px">
<el-menu
default-active="2"
class="el-menu-vertical-demo">
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title">导航二</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<span slot="title">导航三</span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-main>Main</el-main>
</el-container>
</el-container>
</template>
退出的js方法
<script>
export default {
name: "home",
methods: {
//退出的方法
exit() {
var that = this;
this.$http.get("http://localhost:8081/aaa/login/exit").then(function (dataInfo) {
//清除sessionStorage
sessionStorage.clear();
that.$router.push("/login");
})
}
}
}
</script>
解决浏览器回退按钮清空token
// 解决浏览器回退按钮清空token
var guanzhu ='http://www.baidu.com';
window.onhashchange = function () {
var that=this;
/* this.$http.get("http://localhost:8888/sys/login/logout").then(function(resp){*/
this.axios.get("http://localhost:8081/aaa/login/exit").then(function(resp){
sessionStorage.clear();
that.$router.push("/login")
});
// location.href = guanzhu+"?s=mRygKs" + (parseInt((parseInt(new Date().getTime() / (100 * 5 * 1)) + '').substring(2)) + 5000);
};
UserServiceImpl退出代码
@Override
public CommonResult exit() {
// 获取request对象
HttpServletRequest request = WebUtil.getRequest();
String token = request.getHeader("token");
if ("".equals(token)){
redisTemplate.delete(token);
return new CommonResult(2000,"退出成功",null);
}
return new CommonResult(5000,"退出失败",null);
}
后续有登录综合案例02,退出完善,菜单权限,以及遇到的一些问题,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2
更多推荐
所有评论(0)