1. Vue通过脚手架创建Vue工程。
1.组件【网页】--->组件【父组件】可以引用另一个组件[子组件].父组件怎么传参给子组件
2.路由:
[1]路由跳转
() this.$router.push("/路由路径")
路由配置: {path:"",component:""}
路由渲染:理解:
权限系统:
1.前端使用: vue + elementui + axios + css + html
2.后端使用: springboot+mybatis-plus +mybatis+druid+shiro+swagger2+redis
(1)Login.vue组件
用户登录
User login
登录
(2)配置路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Login from '../views/Login'
Vue.use(VueRouter)
const routes = [
{
/*路由路径为/则重定向到/login路由*/
path: '/',
redirect: '/login'
},
{
path: '/login',
name: 'Login',
component: Login
//component: ()=>import("../views/Login.vue")
},
]
const router = new VueRouter({
//删除地址栏的#
mode: 'history',
routes
})
export default router
(3)登录按钮事件
如果想在vue工程中使用axios进行异步请求,则需要在main.js中导入axios
[1]//导入axiosimport axios from "axios";
[2]//把axios挂载到vue对象中,以后在vue中如果使用axios直接可以用$http名称,可以随意起名
Vue.prototype.$http=axios
methods:{
handleSubmit(){
//表单校验
this.$refs['ruleForm'].validate((valid) => {
if (valid) {
//this.$http就是自己刚刚定义的axios
//url:后端登录接口的路径 //es=>函数
this.$http.post("http://localhost:8081/system/login",this.ruleForm).then(result=>{
})
}
})
}
}
(1) 创建springboot项目
pox.xml文件依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.12.RELEASE
com.wzh
springboot-vue0808
0.0.1-SNAPSHOT
springboot-vue0808
Demo project for Spring Boot
1.8
com.baomidou
mybatis-plus-generator
3.5.2
org.freemarker
freemarker
2.3.31
com.baomidou
mybatis-plus-boot-starter
3.5.2
com.alibaba
druid-spring-boot-starter
1.2.8
com.spring4all
swagger-spring-boot-starter
1.9.1.RELEASE
com.github.xiaoymin
swagger-bootstrap-ui
1.9.6
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.2
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
(2) 使用mybatis-plus的代码生成器
package com.wzh;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
/**
* @ProjectName: springboot-vue0808
* @Package: com.wzh
* @ClassName: Generator
* @Author: 王振华
* @Description: mp代码生成器
* @Date: 2022/8/8 18:33
* @Version: 1.0
*/
public class Generator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/shiro_permission?serverTimezone=Asia/Shanghai", "root", "123456")
.globalConfig(builder -> {
builder.author("王振华") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir(".\\src\\main\\java\\"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.wzh") // 设置父包名
.moduleName("system") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "src\\main\\resources\\mapper\\")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("acl_user","acl_role","acl_permission")// 设置需要生成的表名
.addTablePrefix("acl_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
(3)配置application文件
#端口号
server.port=8081
#druid数据源
spring.datasource.druid.url=jdbc:mysql://localhost:3306/shiro_permission?serverTimezone=Asia/Shanghai
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
#初始化的个数
spring.datasource.druid.initial-size=5
# 最大活跃数
spring.datasource.druid.max-active=10
# 最大等待时间
spring.datasource.druid.max-wait=3000
# 最小的闲置个数
spring.datasource.druid.min-idle=5
#mybatis-plus日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(4)登录接口
package com.wzh.system.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wzh.system.entity.User;
import com.wzh.system.service.IUserService;
import com.wzh.system.vo.CommonResult;
import com.wzh.system.vo.LoginVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @ProjectName: springboot-vue0808
* @Package: com.wzh.system.controller
* @ClassName: LoginController
* @Author: 王振华
* @Description:登录接口
* @Date: 2022/8/8 18:47
* @Version: 1.0
*/
@RestController
@RequestMapping("system")
@Api(tags = "登录的接口类")
//@CrossOrigin //解决跨域问题
public class LoginController {
@Autowired
private IUserService userService;
@PostMapping("/login")
@ApiOperation(value = "登录接口")
public CommonResult login(@RequestBody LoginVo loginVo){
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("username",loginVo.getUsername());
wrapper.eq("password",loginVo.getPassword());
wrapper.eq("is_deleted",0);
User user = userService.getOne(wrapper);
if(user!=null){
return new CommonResult(2000,"登录成功",null);
}else{
return CommonResult.LOGIN_ERROR;
}
}
}
vo包:它也是实体类的一种; view obeject 视图对象。 作用:接受和响应网页的对象,
如果用map接收的话,无法使用swagger注解
前端调用后端登录接口时出现如下的错误
当使用异步请求从一个网址访问另一个网址时可能会出现跨域问题。
前提:
1. 必须为异步请求
2. 当端口号或协议或ip不同时则会出现跨域
出现两个请求: 有一个请求的方式为: OPTIONS 和真实的请求方式
理解: OPTIONS先头部队。---探视后台有没有解决跨域。
如何解决跨域:
1.前端解决
2.后端解决---->这里也有几种方式:
【1】可以借助nginx.
【2】在代码中解决 (重点)
在控制层接口上添加@CrossOrigin
@CrossOrigin(origins = {"192.168.1.14:8081","192.168.2.34:8080"},allowedHeaders="运行哪些请求头跨域",methods={RequestMethod.GET,RequestMethod.POST})
(origins = {"192.168.0.111:8080","192.168.0.120:8081"},allowedHeaders="运行哪些请求头跨域",methods={"GET","POST"})
origins: 允许哪些域可以跨域访问我这个接口
allowedHeaders:允许哪些请求头信息跨域
methods: 允许哪些请求方式跨域
上面在控制层接口处加上注解的方式解决跨域,麻烦的地方就需要对每个控制类都加该注解。 设置一个全局跨域配置类。
package com.wzh.system.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
// 当前跨域请求最大有效时长。这里默认1天
private static final long MAX_AGE = 24 * 60 * 60;
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
corsConfiguration.setMaxAge(MAX_AGE);
source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
return new CorsFilter(source);
}
}
(5)加入swagger配置类
package com.wzh.system.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.VendorExtension;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
/**
* @Author 闫克起
* @Date 2021/4/29 16:37
* @Version 1.0
*/
@Configuration
public class SwaggerConfig {
//获取swagger2的实例对象docket
@Bean
public Docket getDocket() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("QY151")
.apiInfo(apiInfo())
.select()//设置哪些包下的类生产api接口文档
.apis(RequestHandlerSelectors.basePackage("com.wzh.system.controller"))
//设置哪些请求路径生产接口文档
.paths(PathSelectors.any())
.build();
return docket;
}
private ApiInfo apiInfo() {
Contact DEFAULT_CONTACT = new Contact("王振华", "http://www.bing.com", "1430930278@qq.com");
ApiInfo apiInfo = new ApiInfo("员工管理系统API接口文档", "员工管理系统API接口文档", "1.0", "http://www.bing.com",
DEFAULT_CONTACT, "Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0", new
ArrayList());
return apiInfo;
}
}
(6)登录成功后前端路由跳转
上面咱们写的登录,后端没有保存数据 前端也没有拿到数据进行保存
要用redis的原因是因为如果高并发访问数据库数据库会崩溃,造成系统瘫痪
org.springframework.boot
spring-boot-starter-data-redis
application配置文件
#redis的配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.jedis.pool.max-active=20
spring.redis.jedis.pool.max-wait=20000
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=5
(1)修改登录的接口
package com.wzh.system.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wzh.system.entity.User;
import com.wzh.system.service.IUserService;
import com.wzh.system.vo.CommonResult;
import com.wzh.system.vo.LoginVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @ProjectName: springboot-vue0808
* @Package: com.wzh.system.controller
* @ClassName: LoginController
* @Author: 王振华
* @Description:登录接口
* @Date: 2022/8/8 18:47
* @Version: 1.0
*/
@RestController
@RequestMapping("system")
@Api(tags = "登录的接口类")
//@CrossOrigin(origins = {"192.168.1.14:8081","192.168.2.34:8080"},allowedHeaders="运行哪些请求头跨域",methods={RequestMethod.GET,RequestMethod.POST}) //解决跨域问题
public class LoginController {
@Autowired
private IUserService userService;
@Autowired
private RedisTemplate redisTemplate;
@PostMapping("/login")
@ApiOperation(value = "登录接口")
public CommonResult login(@RequestBody LoginVo loginVo){
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("username",loginVo.getUsername());
wrapper.eq("password",loginVo.getPassword());
wrapper.eq("is_deleted",0);
User user = userService.getOne(wrapper);
if(user!=null){
//随机生成一个唯一字符串。
String token = UUID.randomUUID().toString();
//把该token作为redis的key value为当前登录用户信息
ValueOperations forValue = redisTemplate.opsForValue();
forValue.set(token,user,24, TimeUnit.HOURS);
return new CommonResult(2000,"登录成功",token);
}else{
return CommonResult.LOGIN_ERROR;
}
}
}
RedisTemplate类需要序列化,加入一个配置类
package com.wzh.system.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.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;
/**
* @program: qy151-redis-springboot
* @description:
* @author: 王振华
* @create: 2022-08-08 15:16
**/
@Configuration
public class RedisConfig {
//这个是用来自己创建的RedisTemplate
/* @Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer 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;
}*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate<>();
RedisSerializer 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序列化 filed value
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(redisSerializer);
return template;
}
}
(2)修改前端登录方法
后面每次请求都可以携带该token,
每次请求都得要人为添加参数token. 我们可以使用axios的请求拦截器。
验证token有没有被使用
获取用户信息
接口:
package com.wzh.system.controller;
import com.wzh.system.entity.User;
import com.wzh.system.vo.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
*
* 用户表 前端控制器
*
*
* @author 王振华
* @since 2022-08-08
*/
@RestController
@RequestMapping("/system/user")
public class UserController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("getInfo")
public CommonResult getInfo(HttpServletRequest request) {
String token = request.getHeader("token"); //获取请求头
System.out.println(token);
//根据token从redis中获取用户信息
ValueOperations forValue = redisTemplate.opsForValue();
User o = (User) forValue.get(token);
return new CommonResult(2000, "获取用户信息成功", o);
}
}
这时发现报500 原因我们的实体类有两个时间类型
解决办法:
com.fasterxml.jackson.datatype
jackson-datatype-jsr310
2.9.3
类属性上加入注解
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
运行程序成功
前置路由守卫:就是在路由跳转前加上自己得一些业务代码,在main.js中配置。类似于拦截器
//设置前置路由守卫 to:到哪个路由 from:从哪个路由来 next():放行到指定路由
router.beforeEach((to,from,next)=>{
//获取跳转得路径
var path = to.path;
//判断是否为登录路由路径
if(path==="/login"){
console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
//放行
return next();
}
//其他路由路径 判断是否登录过
var token = sessionStorage.getItem("token");
if(token){
return next();
}
//跳转登录
return next("/login");
})
(1)添加依赖
org.apache.shiro
shiro-spring-boot-starter
1.7.0
(2)shiro的配置类
package com.wzh.system.config;
import com.wzh.system.filter.LoginFilter;
import com.wzh.system.realm.MyRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.HashMap;
/**
* @ProjectName: springboot-shiro-swagger
* @Package: com.wzh.config
* @ClassName: ShiroConfig
* @Author: 王振华
* @Description:
* @Date: 2022/8/5 17:32
* @Version: 1.0
*/
@Configuration
public class ShiroConfig {
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
return securityManager;
}
@Bean
public Realm realm() {
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(credentialsMatcher());
return myRealm;
}
@Bean
public CredentialsMatcher credentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("MD5");
credentialsMatcher.setHashIterations(1024);
return credentialsMatcher;
}
@Autowired
private RedisTemplate redisTemplate;
@Bean(value = "shiroFilter")
public ShiroFilterFactoryBean filterFactoryBean() {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager());
//设置拦截规则
HashMap map = new HashMap<>();
//放行路径
map.put("/system/login", "anon");
map.put("/doc.html", "anon");
map.put("/swagger-ui.html", "anon");
map.put("/swagger/**", "anon");
map.put("/webjars/**", "anon");
map.put("/swagger-resources/**", "anon");
map.put("/v2/**", "anon");
map.put("/static/**", "anon");
//拦截路径
map.put("/**", "authc");
map.put("/login/logout", "logout");
filterFactoryBean.setFilterChainDefinitionMap(map);
//设置自定义认证过滤器
HashMap filterMap = new HashMap();
filterMap.put("authc", new LoginFilter(redisTemplate));
filterFactoryBean.setFilters(filterMap);
return filterFactoryBean;
}
@Bean //注册filter
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setName("shiroFilter");
filterRegistrationBean.setFilter(new DelegatingFilterProxy());
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
//开始shiro注解
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public FilterRegistrationBean vv() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
//设置字符编码
characterEncodingFilter.setEncoding("UTF-8");
//设置强制使用指定字符编码
characterEncodingFilter.setForceEncoding(true);
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(characterEncodingFilter);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
(3)增加一个realm类对象
package com.wzh.system.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wzh.system.entity.Permission;
import com.wzh.system.entity.User;
import com.wzh.system.service.IPermissionService;
import com.wzh.system.service.IRoleService;
import com.wzh.system.service.IUserService;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* @ProjectName: springboot-vue0808
* @Package: com.wzh.system.realm
* @ClassName: MyRealm
* @Author: 王振华
* @Description:
* @Date: 2022/8/9 9:47
* @Version: 1.0
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
private IUserService userService;
@Autowired
private IPermissionService permissionService;
@Autowired
private IRoleService roleService;
//当你进行权限校验时会执行该方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
/* User user = (User) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//根据账号查找该用户具有哪些权限
QueryWrapper
List list = permissionService.getOne(user.get());
if(list!=null&&list.size()>0){
info.addStringPermissions(list);
}
List roles = roleService.findRolesById(user.getUserid());
if(roles!=null&&roles.size()>0){
info.addRoles(roles);
}*/
return null;
}
//该方法用于完成认证的功能
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1.根据token获取账号
String username = (String) token.getPrincipal();
/**
* 以前登陆的逻辑是 把用户和密码全部发到数据库 去匹配
* 在shrio里面是先根据用户名把用户对象查询出来,再来做密码匹配
*/
//2.根据账号查询用户信息
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
wrapper.eq("is_deleted",0);
User user = userService.getOne(wrapper);
//表示该用户名在数据库中存在
if(user!=null){
/**
* 参数说明
* 参数1:可以传到任意对象
* 参数2:从数据库里面查询出来的密码
* 参数3:盐
* 参数4:当前类名
*/
ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),credentialsSalt,this.getName());
return info;
}
return null;
}
}
(4) 修改controller代码
package com.wzh.system.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wzh.system.entity.User;
import com.wzh.system.service.IUserService;
import com.wzh.system.vo.CommonResult;
import com.wzh.system.vo.LoginVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @ProjectName: springboot-vue0808
* @Package: com.wzh.system.controller
* @ClassName: LoginController
* @Author: 王振华
* @Description:登录接口
* @Date: 2022/8/8 18:47
* @Version: 1.0
*/
@RestController
@RequestMapping("system")
@Api(tags = "登录的接口类")
//@CrossOrigin(origins = {"192.168.1.14:8081","192.168.2.34:8080"},allowedHeaders="运行哪些请求头跨域",methods={RequestMethod.GET,RequestMethod.POST}) //解决跨域问题
public class LoginController {
@Autowired
private IUserService userService;
@Autowired
private RedisTemplate redisTemplate;
@PostMapping("/login")
@ApiOperation(value = "登录接口")
public CommonResult login(@RequestBody LoginVo loginVo){
try{
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginVo.getUsername() ,loginVo.getPassword());
subject.login(usernamePasswordToken);
Object user = subject.getPrincipal();
//随机生成一个唯一字符串。
String token = UUID.randomUUID().toString();
//把该token作为redis的key value为当前登录用户信息
ValueOperations forValue = redisTemplate.opsForValue();
forValue.set(token,user,24, TimeUnit.HOURS);
return new CommonResult(2000,"登录成功",token);
}catch (Exception e){
e.printStackTrace();
return CommonResult.LOGIN_ERROR;
}
}
}
测试登录
登录成功后获取用户信息时出现如下得错误
被shiro的拦截器给拦截了。
package com.wzh.system.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzh.system.vo.CommonResult;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
/**
* @ProjectName: springboot-vue0808
* @Package: com.wzh.system.filter
* @ClassName: LoginFilter
* @Author: 王振华
* @Description:
* @Date: 2022/8/9 9:46
* @Version: 1.0
*/
//如果类没有交于spring容器来管理 那么该类中得属性也不能让spring帮你注入
public class LoginFilter extends FormAuthenticationFilter {
private RedisTemplate redisTemplate; //LoginFilter必须交于spring容器来管理。
public LoginFilter(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
//当登录成功后执行得方法,如果该方法返回false,则执行onAccessDenied
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
HttpServletRequest req = (HttpServletRequest) request;
//1.请求方式是否为OPTIONS
String options = req.getMethod();
if(options!=null && options.equals("OPTIONS")){
return true;
}
String token = req.getHeader("token");
//token可以伪造,所以也要查询redis里有没有该记录
if(token!=null && redisTemplate.hasKey(token)){
return true;
}
return false;
}
//未登录时调用该方法? 为什么进入没有登录方法:
// --->第一个请求是OPTIONS,没有携带token 第二个因为前端和后端不是用得同一个session.默认shiro以sessionId为是否登录得标准
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
CommonResult commonResult = CommonResult.UNLOGIN;
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(commonResult);
writer.print(json);
writer.flush();
writer.close();
return false;
}
}
个人信息
退出登录
Footer
前端:
由于每个跳转都需要写ip和端口,我们可以在main.js中设置axios的基础路径
//设置axios基础路径 axios.defaults.baseURL="http://localhost:8082"
后端:
@GetMapping("/logout")
public CommonResult logout(HttpServletRequest request){
String token = request.getHeader("token");
if(redisTemplate.hasKey(token)){
redisTemplate.delete(token);
return new CommonResult(2000,"退出成功",null);
}
return new CommonResult(5000,"请先登录",null);
}
(1)前端
个人信息
退出登录
{{menu.name}}
{{second.name}}
Footer
(2)后端:
@RestController
@RequestMapping("/system/permission")
public class PermissionController {
@Autowired
private IPermissionService permissionService;
@GetMapping("leftMenu")
public CommonResult leftMenu(HttpServletRequest request){
String token = request.getHeader("token");
return permissionService.findPermissionByUserId(token);
}
}
service
package com.wzh.system.service.impl;
import com.wzh.system.entity.Permission;
import com.wzh.system.entity.User;
import com.wzh.system.mapper.PermissionMapper;
import com.wzh.system.service.IPermissionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzh.system.vo.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
*
* 权限 服务实现类
*
*
* @author 王振华
* @since 2022-08-09
*/
@Service
public class PermissionServiceImpl extends ServiceImpl implements IPermissionService {
@Autowired
private PermissionMapper permissionMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public CommonResult findPermissionByUserId(String token) {
//根据token获取用户信息
ValueOperations forValue = redisTemplate.opsForValue();
User user = (User) forValue.get(token);
//根据用户id查询该用户具有的权限。
List permissionList = permissionMapper.selectByUserId(user.getId());
//设置层级关系
List firstMenus = new ArrayList<>();
for (Permission firstMenu : permissionList){
if(firstMenu.getPid().equals("1")){
firstMenus.add(firstMenu);
}
}
//为一级菜单设置二级菜单
for (Permission first : firstMenus){
//根据一级菜单id 查询 该菜单得二级菜单。如果出现不确定有几级菜单 那么我们可以使用方法得递归调用
first.setChildren(findChildren(permissionList, first.getId()));
}
return new CommonResult(2000,"查询成功",firstMenus);
}
//方法递归
private List findChildren(List permissionList, String pid) {
List children = new ArrayList<>();
for (Permission p : permissionList){
if(p.getPid().equals(pid)){
children.add(p);
}
}
for(Permission child : children){
child.setChildren(findChildren(permissionList,child.getId()));
}
return children;
}
}
实体类添加列
推荐使用下一篇文章的,用到了递归,不管几级菜单都可以