博客管理后台(码神之路)

文章目录

  • 四、管理后台搭建
    • 1.搭建项目
    • 2.页面讲解
    • 3. 权限表的增删改查
    • 4.Security集成
    • 5.登录认证
    • 6.权限认证
    • **补充用户表及权限表**
    • 总结
    • **补充用户表及权限表**
    • 总结

四、管理后台搭建

SpringSecurity

1.搭建项目

1.1 新建子模块maven工程 blog-admin

导入相关maven



    
        blog-parent
        com.lum
        1.0-SNAPSHOT
    
    4.0.0

    blog-admin

    
        8
        8
    
    
        
            org.springframework.boot
            spring-boot-starter
            
            
                
                    org.springframework.boot
                    spring-boot-starter-logging
                
            
        

        
        
            org.springframework.boot
            spring-boot-starter-log4j2
        

        
            org.springframework.boot
            spring-boot-starter-aop
        

        
            org.springframework.boot
            spring-boot-starter-mail
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            com.alibaba
            fastjson
            1.2.76
        

        
            mysql
            mysql-connector-java
        

        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

        
            org.apache.commons
            commons-lang3
        

        
            commons-collections
            commons-collections
            3.2.2
        
        
            commons-codec
            commons-codec
        

        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.3
        
        
            org.projectlombok
            lombok
        
        
            joda-time
            joda-time
            2.10.10
        




    


创建启动类

package com.lum.blog.admin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author lum
 * @date 2021/9/7
 */
@SpringBootApplication
public class AdminApp {
    public static void main(String[] args) {
        SpringApplication.run(AdminApp.class,args);
    }
}

MybatisPlus进行配置

package com.lum.blog.admin.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.lum.blog.admin.mapper")
public class MyBatisPlusConfig {

    //Mybatis-plus分页插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor( new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

将管理后台静态资源(gitee中)复制到resource下创建的static中

启动测试

博客管理后台(码神之路)_第1张图片

2.页面讲解

main.html是用vue+elementui开发

elementui是基于vue需要导入vue包

    <link rel="stylesheet" href="../plugins/elementui/index.css">
    <link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">

导入vue相关及ajax


<script src="../js/vue.js">script>
<script src="../plugins/elementui/index.js">script>
<script type="text/javascript" src="../js/jquery.min.js">script>
<script src="../js/axios-0.18.0.js">script>

【码神之路】项目实战教程,springboot+vue练手级项目,真实的在线博客系统,十年大厂程序员讲解,从易到难,循序渐进_哔哩哔哩_bilibili

3. 权限表的增删改查

用到ms_admin,ms_permission,ms_admin_permission

刷新页面发现传递的参数

博客管理后台(码神之路)_第2张图片

permission.html中

博客管理后台(码神之路)_第3张图片

controller中新建AdminController添加增删改查方法

package com.lum.blog.admin.comtroller;

import com.lum.blog.admin.model.params.PageParam;
import com.lum.blog.admin.pojo.Permission;
import com.lum.blog.admin.service.PermissionService;
import com.lum.blog.admin.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author lum
 * @date 2021/9/7
 */

@RestController
@RequestMapping("admin")
public class AdminController {
    @Autowired
    private PermissionService permissionService;

    @PostMapping("permission/permissionList")
    public Result listPermission(@RequestBody PageParam pageParam) {
        return permissionService.listPermission(pageParam);
    }

    @PostMapping("permission/add")
    public Result add(@RequestBody Permission permission){
        return permissionService.add(permission);
    }

    @PostMapping("permission/update")
    public Result update(@RequestBody Permission permission){
        return permissionService.update(permission);
    }

    @GetMapping("permission/delete/{id}")
    public Result delete(@PathVariable("id") Long id){
        return permissionService.delete(id);
    }
}

创建PermissionService

package com.lum.blog.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lum.blog.admin.mapper.PermissionMapper;
import com.lum.blog.admin.model.params.PageParam;
import com.lum.blog.admin.pojo.Permission;
import com.lum.blog.admin.vo.PageResult;
import com.lum.blog.admin.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author lum
 * @date 2021/9/7
 */
@Service
public class PermissionService {

    @Autowired
    private PermissionMapper permissionMapper;

    public Result listPermission(PageParam pageParam) {
        /**
         * 要的数据,管理台表的所有字段 permission
         * 要的分页查询
         */
        Page<Permission> page = new Page<>(pageParam.getCurrentPage(), pageParam.getPageSize());
        LambdaQueryWrapper<Permission> queryWrapper = new LambdaQueryWrapper<>();
        //判断查询条件不为空,进行分页
        if (StringUtils.isNotBlank(pageParam.getQueryString())) {
            queryWrapper.eq(Permission::getName, pageParam.getQueryString());
        }
        //查询page
        Page<Permission> permissionPage = permissionMapper.selectPage(page,queryWrapper);
        PageResult<Permission> pageResult = new PageResult<>();

        pageResult.setList(permissionPage.getRecords());
        pageResult.setTotal(permissionPage.getTotal());
        return Result.success(pageResult);
    }

    public Result add(Permission permission) {
        this.permissionMapper.insert(permission);
        return Result.success(null);
    }

    public Result update(Permission permission) {
        this.permissionMapper.updateById(permission);
        return Result.success(null);
    }

    public Result delete(Long id) {
        this.permissionMapper.deleteById(id);
        return Result.success(null);
    }
}

mapper创建permissionMapper接口

package com.lum.blog.admin.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.admin.pojo.Permission;

/**
 * @author lum
 * @date 2021/9/7
 */
public interface PermissionMapper extends BaseMapper<Permission> {
}

所需:

pojo.Permission

package com.lum.blog.admin.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

/**
 * @author lum
 * @date 2021/9/7
 */
@Data
public class Permission {
    //设置id为自增id,无需分布式id

    @TableId(type = IdType.AUTO)
    private Long id;

    private String name;

    private String path;

    private String description;

}

moudle.params.PageParam

package com.lum.blog.admin.model.params;

import lombok.Data;

/**
 * @author lum
 * @date 2021/9/7
 */
@Data
public class PageParam {

    private Integer currentPage;

    private Integer pageSize;

    private String queryString;
}

vo.Result

package com.lum.blog.admin.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//承担着返回首页文章列表的类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
    //代表是否成功
    private boolean success;

    //代表我们的编码
    private int code;

    //代表的是消息
    private String msg;

    //代表的是数据
    private Object data;


    //代表成功
    public static Result success(Object data) {
        return new Result(true , 200, "success" ,data);
    }

    //代表失败的
    public static Result fail(int code, String msg) {
        //没有数据可以返回,所有data是null
        return new Result(false , code, msg,null);
    }

}

vo.PageResult

package com.lum.blog.admin.vo;

import lombok.Data;

import java.util.List;

/**
 * @author lum
 * @date 2021/9/7
 */
@Data
public class PageResult {

    private List list;

    private Long total;
}

进行各个方法测试

4.Security集成

4.1添加依赖

<dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-securityartifactId>
        dependency>

4.2创建配置文件

config.SecuiityConfig

package com.lum.blog.admin.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @author lum
 * @date 2021/9/7
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //密码配置 BCrypt密码加密策略
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }


    public static void main(String[] args) {
        //加密策略 MD5不安全 彩虹表 MD5加盐
        String lum = new BCryptPasswordEncoder().encode("lum");
        System.out.println(lum);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //开启登录验证
                .antMatchers("/user/findAll").hasRole("admin")
                //访问接口需要admin角色
                //对应放行
                .antMatchers("/css/**").permitAll()
                .antMatchers("/img/**").permitAll()
                .antMatchers("/js/**").permitAll()
                .antMatchers("/plugins/**").permitAll()

                .antMatchers("/admin/**").access("@authService.auth(request,authentication)")      //自定义Service来实现实时权限认证,要通过uthService认证  通过放行
                .antMatchers("/pages/**").authenticated()
                .and().formLogin()
                .loginPage("/login.html")             //自定义登录界面
                .loginProcessingUrl("/login")         //登录处理接口
                .usernameParameter("username")       //定义登录时的用户名的key 默认username
                .passwordParameter("password")      //定义登录时的密码的key 默认password
                .defaultSuccessUrl("/pages/main.html")
                .failureUrl("/login.html")
                .permitAll()                  //通过 不拦截没根据前面配的路径决定,这是指和登录表单相关的接口  都通过
                .and().logout()              //退出登录配置
                .logoutUrl("/logout")       //退出登录接口
                .logoutSuccessUrl("/login.html")
                .permitAll()                //退出登录的接口放行
                .and()
                .httpBasic()                //用http(postman)访问进行拦截
                .and()
                .csrf().disable()           //crf关闭 如果自定义登录 需要关闭
                .headers().frameOptions().sameOrigin();  //支持iframe页面嵌套
            }
}

5.登录认证

创建pojo.Admin

package com.lum.blog.admin.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

/**
 * @author lum
 * @date 2021/9/7
 */
@Data
public class Admin {

    @TableId(type = IdType.AUTO)
    private Long id;

    private String username;

    private String password;
}

创建service.SecurityUserService

package com.lum.blog.admin.service;

import com.lum.blog.admin.pojo.Admin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
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.Component;

import java.util.ArrayList;

/**
 * @author lum
 * @date 2021/9/7
 */
@Component
public class SecurityUserService implements UserDetailsService {

    @Autowired
    private AdminService adminService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //登录时会把username传递到这里
        //通过username查询出admin表,如果存在,将密码告诉spring security
        //如果不存在 返回null 认证失败
       Admin admin  = this.adminService.findAdminByUsername(username);
        if (admin == null) {
            //登录失败
            return null;
        }
        //交给Security
        UserDetails userDetails = new User(username, admin.getPassword(),new ArrayList<>());
        return userDetails;
    }
}

创建service.AdminService

package com.lum.blog.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.lum.blog.admin.mapper.AdminMapper;
import com.lum.blog.admin.pojo.Admin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author lum
 * @date 2021/9/7
 */
@Service
public class AdminService {
    @Autowired
    private AdminMapper adminMapper;

    public Admin findAdminByUsername(String username){
        LambdaQueryWrapper<Admin> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Admin::getUsername,username);
        queryWrapper.last("limit 1");
        Admin admin = adminMapper.selectOne(queryWrapper);
        return admin;

    }
}

service.AuthService

package com.lum.blog.admin.service;

import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;

/**
 * @author lum
 * @date 2021/9/7
 */
@Service
public class AuthService {

    public boolean auth(HttpServletRequest request, Authentication authentication){
        return true;
    }
}

创建mapper.AdminMapper

package com.lum.blog.admin.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.admin.pojo.Admin;

/**
 * @author lum
 * @date 2021/9/7
 */
public interface AdminMapper extends BaseMapper {
}

登录测试

博客管理后台(码神之路)_第4张图片

博客管理后台(码神之路)_第5张图片

6.权限认证

AdminService 创建findPermissionByAdminId

package com.lum.blog.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.lum.blog.admin.mapper.AdminMapper;
import com.lum.blog.admin.pojo.Admin;
import com.lum.blog.admin.pojo.Permission;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author lum
 * @date 2021/9/7
 */
@Service
public class AdminService {
    @Autowired
    private AdminMapper adminMapper;

    public Admin findAdminByUsername(String username){
        LambdaQueryWrapper<Admin> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Admin::getUsername,username);
        queryWrapper.last("limit 1");
        Admin admin = (Admin) adminMapper.selectOne(queryWrapper);
        return admin;

    }

    //按照管理员id查找权限

    public List<Permission> findPermissionByAdminId(Long adminId) {
        return adminMapper.findPermissionByAdminId(adminId);
    }
}

AuthService 进行权限的认证

package com.lum.blog.admin.service;

import com.lum.blog.admin.pojo.Admin;
import com.lum.blog.admin.pojo.Permission;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author lum
 * @date 2021/9/7
 */
@Service
public class AuthService {

    @Autowired
    private AdminService adminService;

    public boolean auth(HttpServletRequest request, Authentication authentication){
        //权限认证
        //请求路径
        String requestURI = request.getRequestURI();
        Object principal = authentication.getPrincipal();
        if (principal == null || "anonymousUser".equals(principal)) {
            //未登录
            return false;
        }
        UserDetails userDetails = (UserDetails) principal;
        String username = userDetails.getUsername();
        Admin admin = adminService.findAdminByUsername(username);
        if (admin == null) {
            return false;
        }
        if (1 == admin.getId()){
            //超级管理员
            return true;
        }
        Long id = admin.getId();
        List<Permission> permissionsList = this.adminService.findPermissionByAdminId(id);
        //有可能有?传参取0位
        requestURI = StringUtils.split(requestURI, "1234567890")[0];
        for (Permission permission : permissionsList) {
            //如果响应Uri与权限信息path相同,放行
            if (requestURI.equals(permission.getPath())){
                return true;
            }
        }
        return false;
    }
}

添加按照管理员id找权限方法

package com.lum.blog.admin.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.admin.pojo.Admin;
import com.lum.blog.admin.pojo.Permission;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @author lum
 * @date 2021/9/7
 */
public interface AdminMapper extends BaseMapper<Admin> {
    /**
     * 按管理员id查找权限
     * @param adminId
     * @return
     */
    @Select("select * from ms_permission where id in(select permission_id from ms_admin_permission where admin_id=#{adminId})")
    List<Permission> findPermissionByAdminId(Long adminId);
}

博客管理后台(码神之路)_第6张图片

补充用户表及权限表

每个用户都有角色,每个角色拥有什么权限

总结

  1. jwt + redis

    token令牌的登录方式,访问速度快,session共享,安全性

    redis做了令牌和用户信息的对应管理, 1.进一步增加了安全性 2.登录用户做了缓存,灵活 控制用户的过期(续期,踢掉线等)

  2. ThreadLocal 使用保存用户信息,请求的线程之内,可以随时获取登录的用户,做了线程隔离

  3. 使用完ThreadLocal之后,做了value删除,防止内存泄露

  4. 线程安全-update table set value = newValue where id=1 and value=oldValue

    旧值的基础上更新新值,防止并发出现的问题

  5. 线程池 应用非常广,七个核心参数(对当前的主业务 无影响的操作,放入线程池执行)

    登录,记录日志(要求互不影响)

  6. 权限系统

  7. 统一日志记录,统一缓存处理

public interface AdminMapper extends BaseMapper {
/**
* 按管理员id查找权限
* @param adminId
* @return
*/
@Select(“select * from ms_permission where id in(select permission_id from ms_admin_permission where admin_id=#{adminId})”)
List findPermissionByAdminId(Long adminId);
}

博客管理后台(码神之路)_第7张图片

补充用户表及权限表

每个用户都有角色,每个角色拥有什么权限

总结

  1. jwt + redis

    token令牌的登录方式,访问速度快,session共享,安全性

    redis做了令牌和用户信息的对应管理, 1.进一步增加了安全性 2.登录用户做了缓存,灵活 控制用户的过期(续期,踢掉线等)

  2. ThreadLocal 使用保存用户信息,请求的线程之内,可以随时获取登录的用户,做了线程隔离

  3. 使用完ThreadLocal之后,做了value删除,防止内存泄露

  4. 线程安全-update table set value = newValue where id=1 and value=oldValue

    旧值的基础上更新新值,防止并发出现的问题

  5. 线程池 应用非常广,七个核心参数(对当前的主业务 无影响的操作,放入线程池执行)

    登录,记录日志(要求互不影响)

  6. 权限系统(SpringSecurity)

  7. 统一日志记录,统一缓存处理

你可能感兴趣的:(博客项目,spring,boot,java,spring)