1.前端使用: vue + elementui + axios + css + html
2.后端使用: springboot+mybatis-plus +mybatis+druid+shiro+swagger2+redis
登录
后端接口:
package com.qy151.system.controller;
import com.qy151.system.entity.User;
import com.qy151.system.vo.CommonResult;
import io.swagger.annotations.Api;
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.HttpServlet;
import javax.servlet.http.HttpServletRequest;
/**
*
* 用户表 前端控制器
*
*
* @author YSH
* @since 2022-08-11
*/
@RestController
@RequestMapping("/system/user")
@Api(tags = "用户接口类")
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);
}
}
前置路由守卫:就是在路由跳转前加上自己得一些业务代码,在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");
})
org.apache.shiro
shiro-spring-boot-starter
1.7.0
package com.qy151.system.config;
import com.qy151.system.filter.LoginFilter;
import com.qy151.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.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.HashMap;
/**
* @BelongsProject: 0805-springboot-shiro
* @BelongsPackage: com.qy151.config
* @unthor : YSH
* @date : 2022/8/5 18:52
* @Description: TODO
*/
@Configuration //交于容器管理
public class ShiroConfig {
//安全认证
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(realm());
return securityManager;
}
//安全数据源 自定义realm
@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;
//shiro过滤器工厂 设置过滤的规则
@Bean(value = "shiroFilter")
public ShiroFilterFactoryBean filterFactoryBean(){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.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");
factoryBean.setFilterChainDefinitionMap(map);
//设置自定义认证过滤器
HashMap filterMap=new HashMap();
filterMap.put("authc",new LoginFilter(redisTemplate));
factoryBean.setFilters(filterMap);
return factoryBean;
}
//注册filter
@Bean
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;
}
}
package com.qy151.system.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qy151.system.entity.User;
import com.qy151.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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @BelongsProject: 0805-springboot-shiro
* @BelongsPackage: com.qy151.realm
* @unthor : YSH
* @date : 2022/8/5 18:57
* @Description: TODO
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
private IUserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
wrapper.eq("is_deleted",0);
User user = userService.getOne(wrapper);
if(user!=null){
ByteSource bytes = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),bytes,this.getName());
return info;
}
return null;
}
}
package com.qy151.system.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qy151.system.entity.User;
import com.qy151.system.service.IUserService;
import com.qy151.system.vo.CommonResult;
import com.qy151.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 java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @BelongsProject: 0811qxglxt
* @BelongsPackage: com.qy151.system.controller
* @unthor : YSH
* @date : 2022/8/11 16:17
* @Description: TODO
*/
@RestController
@RequestMapping("system")
@Api(tags = "登录接口类")
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.getName(), loginVo.getPassword(),loginVo.getSalt());
subject.login(usernamePasswordToken);
Object one = subject.getPrincipal();
String token = UUID.randomUUID().toString();
ValueOperations forValue = redisTemplate.opsForValue();
forValue.set(token,one,24,TimeUnit.HOURS);
return new CommonResult(2000,"登录成功",token);
}catch (Exception e){
return new CommonResult(5000,"登录失败",null);
}
}
}
登录成功后获取用户信息时出现如下得错误
被shiro得拦截器给拦截器了。
package com.qy151.system.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qy151.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 javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
/**
* @BelongsProject: 0805-springboot-shiro
* @BelongsPackage: com.qy151.filter
* @unthor : YSH
* @date : 2022/8/5 19:19
* @Description: TODO
*/
//如果类没有交于spring容器来管理 那么该类中得属性也不能让spring帮你注入
public class LoginFilter extends FormAuthenticationFilter {
@Autowired
private RedisTemplate redisTemplate; //LoginFilter必须交于spring容器来管理。
public LoginFilter(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
//当登录成功后执行的方法 如果该方法返回false 则执行onAccessDenied
@Override
protected boolean isAccessAllowed(ServletRequest request,ServletResponse response,Object mappedValue){
System.out.println(redisTemplate);
HttpServletRequest req = (HttpServletRequest) request;
//判断请求方式是否为OPTIONS
String method = req.getMethod();
if (method!=null && method.equals("OPTIONS")){
return true;
}
//判断请求头是否有token值
String token = req.getHeader("token");
if (token!=null && redisTemplate.hasKey(token)){
return true;
}
//返回true则表示登录 返回false则表示未登录
System.out.println("=====================");
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 = new CommonResult(4001, "未登录", null);
ObjectMapper objectMapper=new ObjectMapper();
String json = objectMapper.writeValueAsString(commonResult);
writer.print(json); //响应给客户json数据
writer.flush();
writer.close();
return false;
}
}
个人信息
退出登录
由于每个跳转都需要写ip和端口,我们可以在main.js中设置axios的基础路径
//设置axios基础路径 axios.defaults.baseURL="http://localhost:8809"
@GetMapping("logout")
@ApiOperation(value = "退出接口")
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);
}
initLeftMenu(){
this.$http.get("/system/permission/leftMenu").then(result=>{
if(result.data.code===2000){
this.leftMenus=result.data.data;
}
})
},
package com.qy151.system.controller;
import com.qy151.system.service.IPermissionService;
import com.qy151.system.vo.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
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 YSH
* @since 2022-08-11
*/
@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);
}
}
package com.qy151.system.service;
import com.qy151.system.entity.Permission;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qy151.system.vo.CommonResult;
/**
*
* 权限 服务类
*
*
* @author YSH
* @since 2022-08-11
*/
public interface IPermissionService extends IService {
CommonResult findPermissionByUserId(String token);
}
package com.qy151.system.service.impl;
import com.qy151.system.entity.Permission;
import com.qy151.system.entity.User;
import com.qy151.system.mapper.PermissionMapper;
import com.qy151.system.service.IPermissionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qy151.system.service.IRolePermissionService;
import com.qy151.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 YSH
* @since 2022-08-11
*/
@Service
public class PermissionServiceImpl extends ServiceImpl implements IPermissionService {
@Autowired
private PermissionMapper permissionMapper;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private IRolePermissionService rolePermissionService;
@Override
public CommonResult findPermissionByUserId(String token) {
//根据token获取用户信息
ValueOperations forValue = redisTemplate.opsForValue();
User o = (User) forValue.get(token);
String id = o.getId();
//根据用户id查询该用户具有得权限。
List permissionList = permissionMapper.selectByUserId(id);
//设置层级关系
List firstMenus = new ArrayList<>();
for (Permission first : permissionList) {
//int
if (first.getPid().equals("1")) {
firstMenus.add(first);
}
}
//为一级菜单设置二级菜单
for (Permission first : firstMenus) {
//根据一级菜单id 查询 该菜单得二级菜单。如果出现不确定有几级菜单 那么我们可以使用方法得递归调用
first.setChildren(findChildren(permissionList, first.getId()));
}
return new CommonResult(2000,"查询成功",firstMenus);
}
//方法递归
private List findChildren(List permissionList, String id) {
List children = new ArrayList<>();
for (Permission p : permissionList) {
if (p.getPid().equals(id)) {
children.add(p);
}
}
for (Permission child : children) {
child.setChildren(findChildren(permissionList, child.getId()));
}
return children;
}
}
查询全部角色信息时,添加了分页插件
插件的配置类:
package com.qy151.system.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
@PostMapping("findRole/{currentPage}/{pageSize}")
@ApiOperation(value = "查询接口")
public CommonResult findRole(@PathVariable Integer currentPage, @PathVariable Integer pageSize, @RequestBody RoleVo roleVo){
return roleService.selectRole(currentPage,pageSize,roleVo);
}
CommonResult selectRole(Integer currentPage, Integer pageSize, RoleVo roleVo);
@Autowired
private RoleMapper roleMapper;
@Override
public CommonResult selectRole(Integer currentPage, Integer pageSize, RoleVo roleVo) {
Page page = new Page<>(currentPage,pageSize);
QueryWrapper wrapper = new QueryWrapper<>();
if (StringUtils.hasText(roleVo.getRoleName())){
wrapper.like("role_name",roleVo.getRoleName());
}
if (StringUtils.hasText(roleVo.getStartDate())){
wrapper.ge("gmt_create",roleVo.getStartDate());
}
if (StringUtils.hasText(roleVo.getEndDate())){
wrapper.le("gmt_create",roleVo.getEndDate());
}
roleMapper.selectPage(page,wrapper);
return new CommonResult(2000,"查询成功",page);
}
未完待续!!!!!!