SpringSecurity
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中
启动测试
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
用到ms_admin,ms_permission,ms_admin_permission
刷新页面发现传递的参数
permission.html中
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.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页面嵌套
}
}
创建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 {
}
登录测试
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);
}
每个用户都有角色,每个角色拥有什么权限
jwt + redis
token令牌的登录方式,访问速度快,session共享,安全性
redis做了令牌和用户信息的对应管理, 1.进一步增加了安全性 2.登录用户做了缓存,灵活 控制用户的过期(续期,踢掉线等)
ThreadLocal 使用保存用户信息,请求的线程之内,可以随时获取登录的用户,做了线程隔离
使用完ThreadLocal之后,做了value删除,防止内存泄露
线程安全-update table set value = newValue where id=1 and value=oldValue
旧值的基础上更新新值,防止并发出现的问题
线程池 应用非常广,七个核心参数(对当前的主业务 无影响的操作,放入线程池执行)
登录,记录日志(要求互不影响)
权限系统
统一日志记录,统一缓存处理
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);
}
每个用户都有角色,每个角色拥有什么权限
jwt + redis
token令牌的登录方式,访问速度快,session共享,安全性
redis做了令牌和用户信息的对应管理, 1.进一步增加了安全性 2.登录用户做了缓存,灵活 控制用户的过期(续期,踢掉线等)
ThreadLocal 使用保存用户信息,请求的线程之内,可以随时获取登录的用户,做了线程隔离
使用完ThreadLocal之后,做了value删除,防止内存泄露
线程安全-update table set value = newValue where id=1 and value=oldValue
旧值的基础上更新新值,防止并发出现的问题
线程池 应用非常广,七个核心参数(对当前的主业务 无影响的操作,放入线程池执行)
登录,记录日志(要求互不影响)
权限系统(SpringSecurity)
统一日志记录,统一缓存处理