使用SpringSecurity进行权限管理

关于作者
金山老师:从事Java、大数据、Python职业化培训6年,项目管理、软件开发14年。欢迎添加我的微信号【jshand】,最近建了一些微信交流群,添加【指尖架构师】公众号,回复:进群
部分讲课录屏已经上传到bilibili,欢迎搜索“是金山老师”

权限管理

权限管理

  • 授权
  • 鉴权

授权

给不同的用户身份分配不同的权限(可以操作哪些菜单、按钮)
在这里插入图片描述

  • 用户管理的基础数据管理
  • 菜单的基本信息
  • 角色的基本信息
  • 给角色分配菜单
  • 给用户分配角色

维护完上述信息,开发查询权限菜单的功能

鉴权

接口端需要添加鉴别用户身份是否能够执行某一个请求。

  • shiro : Apache 稍微轻量级的框架
  • SpringSecurity : 扩展性比较好,支持OAUTH2登录等。

角色信息的维护

菜单维护

@TableField(exist = false) // 如果不添加TableField 影响单表的CRUD
private List<Menu> children;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Upf65i1C-1651461037732)(images/权限管理/image-20220424095736984.png)]

  • 查询定级菜单
  • 查询下级菜单

SpringSecurity

https://docs.spring.io/spring-security/site/docs/5.4.10/reference/html5/#samples

认证、授权

入门

创建一个springboot的项目 ,创建一个Controller

  • maven
  • parent
  • 启动器(starter-web)
  • controller
  • app

使用SpringSecurit

添加一个依赖

starter-security

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

springboot项目添加starter-security默认请求必须登录(user +动态密码)

在这里插入图片描述

自动配置使用DelegatingFilterProxy加载Filter

概念

SpringSecurity基于Filter的概念实现的过滤器的功能
在这里插入图片描述

  • 用到了DelegatingFilterProxy类去加载 FilterChain

  • FilterChainProxy:包含一些拦截器 SecurityFilterChain

内置的Filter

  • ChannelProcessingFilter
  • WebAsyncManagerIntegrationFilter
  • SecurityContextPersistenceFilter
  • HeaderWriterFilter
  • CorsFilter
  • CsrfFilter
  • LogoutFilter
  • OAuth2AuthorizationRequestRedirectFilter
  • Saml2WebSsoAuthenticationRequestFilter
  • X509AuthenticationFilter
  • AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • OAuth2LoginAuthenticationFilter
  • Saml2WebSsoAuthenticationFilter
  • UsernamePasswordAuthenticationFilter
  • OpenIDAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • ConcurrentSessionFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RememberMeAuthenticationFilter
  • AnonymousAuthenticationFilter
  • OAuth2AuthorizationCodeGrantFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • SwitchUserFilter
  1. UsernamePasswordAuthenticationFilter:基于用户名密码的登录的过滤器
    1. Authentication
  2. ExceptionTranslationFilter: 登录失败、没有权限
  3. FilterSecurityInterceptor:授权(鉴权)

简单的执行逻辑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QAwGSynk-1651461037738)(images/权限管理/image-20220427142756280.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fid0faNn-1651461037740)(images/权限管理/image-20220427142721850.png)]

配置用户

  • 在配置文件中配置
    • 默认user / 2a096f60-0288-4f9b-bac3-7f1c1bd878b4
server:
  port: 8081


spring:
  security:
    user:
      name: root
      password: 666666

  • Java形式配置

​ 配置PasswordEncoder

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    PasswordEncoder passwordEncoder;

    @Bean
    PasswordEncoder passwordEncoder(){

        return  new BCryptPasswordEncoder();
    }

    //配置认证管理器
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        //构造一个超级管理员的角色
        List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("Admin");

        //$2a$10$Jv1lgSebvvdieROLHeZqR.BzuhxwmbnhtVaAe7YaY6kIEIn2YBu8y
        //noop
//        auth.inMemoryAuthentication().withUser("admin").password("{noop}888888").authorities(authorities);

        String password =  passwordEncoder.encode("666666");
        System.out.println("password = " + password);

        auth.inMemoryAuthentication().withUser("admin").password(password).authorities(authorities);

        //888888 -->转换成密文


        //设置一个类,用户查询数据库
//        auth.userDetailsService()


    }
}

从数据库中查询用户

准备用户表

USE `ssm_java4`;

/*Table structure for table `user` */

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '医生id',
  `username` varchar(100) DEFAULT NULL COMMENT '用户名',
  `password` varchar(100) DEFAULT NULL COMMENT '密码',
  `realname` varchar(100) DEFAULT NULL COMMENT '真实姓名',
  `telephone` varchar(20) DEFAULT NULL COMMENT '电话号码',
  `dept_id` int(11) DEFAULT NULL COMMENT 'id',
  `user_type` int(11) DEFAULT NULL COMMENT '医生类型',
  `lastlogin` datetime DEFAULT NULL COMMENT '最后登录时间',
  `active` int(11) DEFAULT '1' COMMENT '是否有效,1 有效,0 失效',
  `createtime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8mb4 COMMENT='医生-用户信息';

/*Data for the table `user` */

insert  into `user`(`user_id`,`username`,`password`,`realname`,`telephone`,`dept_id`,`user_type`,`lastlogin`,`active`,`createtime`) values (2,'root','123456','真实姓名2222',NULL,NULL,NULL,'2022-04-26 14:36:26',1,'2022-03-15 10:41:51'),(6,'admin','123456','赵云','',NULL,NULL,'2022-04-26 13:36:37',1,'2022-03-15 10:49:42'),(17,'userabc01','12345601','01测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(18,'userabc02','12345602','02测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(19,'userabc03','12345603','03测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(20,'userabc04','12345604','04测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(21,'userabc05','12345605','05测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(22,'userabc06','12345606','06测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(23,'userabc07','12345607','07测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(24,'userabc08','12345608','08测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(25,'userabc09','12345609','09测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(26,'userabc10','12345610','10测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(27,'userabc11','12345611','11测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(28,'userabc12','12345612','12测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(29,'userabc13','12345613','13测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(30,'userabc14','12345614','14测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(31,'userabc15','12345615','15测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(32,'userabc16','12345616','16测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(33,'userabc17','12345617','17测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(34,'userabc18','12345618','18测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(35,'userabc19','12345619','19测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(36,'userabc20','12345620','20测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(37,'userabc21','12345621','21测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(38,'userabc22','12345622','22测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(39,'userabc23','12345623','23测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(40,'userabc24','12345624','24测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(41,'userabc25','12345625','25测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(42,'userabc26','12345626','26测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(43,'userabc27','12345627','27测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(44,'userabc28','12345628','28测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26'),(45,'userabc29','12345629','29测试分页1',NULL,NULL,NULL,NULL,1,'2022-04-02 15:52:26');

整合mybatisplus

  • 添加依赖

    • mybatisplus-starters

    • 数据库的依赖

    • lombok

  • 实体类

  • Mapper接口

  • Mapper.xml

  • 包扫描

  • 实现service

  • 配置数据源

自定义查询数据库的方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m1GRvAtx-1651461037742)(images/权限管理/image-20220428091556381.png)]

  1. 重写UserDetailsService接口, loadUserByUsername, 通过用户名查询数据,返回的接口需要封装到UserDetails
  2. 使用AuthenticationManagerBuilder配置UserDetailsService
package com.neuedu.boot.security.security;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.neuedu.boot.security.entity.User;
import com.neuedu.boot.security.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;


@Service
public class MyUserDetailsService  implements UserDetailsService {

    @Autowired
    IUserService userService;

    /**
     * 根据用户名查询数据库的身份信息
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //根据用户名,查询用户身份信息,
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username",username);
        User user = userService.getOne(wrapper);

        if(user == null){
            throw new UsernameNotFoundException("用户名或者密码不正确,再想想吧");
        }

        return new LoginUser(user);
    }
}

package com.neuedu.boot.security.config;

import com.neuedu.boot.security.security.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.List;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    PasswordEncoder passwordEncoder;

    @Bean
    PasswordEncoder passwordEncoder(){

        return  new BCryptPasswordEncoder();
    }


    @Autowired
    MyUserDetailsService userDetailsService;


    //配置认证管理器
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        //构造一个超级管理员的角色
        List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("Admin");

        //$2a$10$Jv1lgSebvvdieROLHeZqR.BzuhxwmbnhtVaAe7YaY6kIEIn2YBu8y
        //noop
//        auth.inMemoryAuthentication().withUser("admin").password("{noop}888888").authorities(authorities);

//        String password =  passwordEncoder.encode("666666");
//        System.out.println("password = " + password);
//
//        auth.inMemoryAuthentication().withUser("admin").password(password).authorities(authorities);

        //888888 -->转换成密文


        //设置一个类,用户查询数据库
        auth.userDetailsService(userDetailsService);

    }
}

用户认证的时候执行的Filter

UsernamePasswordAuthenticationFilter

Authentication:用户认证的身份

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DxWVCUV9-1651461037744)(images/权限管理/image-20220428095232638.png)]

  • DaoAuthenticationProvider: 负责查询数据
    在这里插入图片描述在这里插入图片描述

整合JWT

  • 重写登录的接口
    • 登录成功生成jwt数据,发送给客户端(vuejs-axios)
  • 解析header中的jwt
    • 每次发送的时候axios中的header会携带 token

自定义登录接口

需要jwt的依赖

<!--整合jwt-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.19.1</version>
</dependency>

定义通用的json类型的返回接口CommonResult

定义LoginController,重写 UsernamePasswordAuthenticationFilter

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5ZRDRIV-1651461037749)(images/权限管理/image-20220429085654931.png)]

package com.neuedu.boot.security.controller;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.neuedu.boot.security.config.CommonResult;
import com.neuedu.boot.security.entity.User;
import com.neuedu.boot.security.security.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.ZoneId;

@RestController
public class LoginController {

    @Autowired
    AuthenticationManager authenticationManager;
    
    @RequestMapping("/user/login")
    CommonResult login(String username,String password){

		//利用SpringSecurity的验证机制
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authenticate = authenticationManager.authenticate(authRequest);

        //如果authenticate为空,则说明登录失败
        if(authenticate != null){

            /**
             * 如果登录成功,生成jwt
             */
            //获取登录状态
            LoginUser loginUser = (LoginUser) authenticate.getPrincipal();

            User user = loginUser.getUser();//对一个数据库的那个实体
            //使用用户身份生成jwt token ,发送个浏览器
            String secret = "abcdef";
            long longLastlogin = user.getLastlogin().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
            String token = JWT.create().
                    withClaim("userId", user.getUserId()).  //设置登录用户id
                    withClaim("username", user.getUsername()).  //设置登录的用户名
                    withClaim("lastlogin", longLastlogin).  //设置最后一次登录时间
                    sign(Algorithm.HMAC256(secret));

            return CommonResult.success(token);
        }else{
            return CommonResult.failed("用户名或密码不正确");
        }

    }



}

配置定义的登录接口

package com.neuedu.boot.security.config;

import com.neuedu.boot.security.security.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.List;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    PasswordEncoder passwordEncoder;

    @Bean
    PasswordEncoder passwordEncoder(){

        return  new BCryptPasswordEncoder();
    }


    @Autowired
    MyUserDetailsService userDetailsService;


    //配置认证管理器
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        //构造一个超级管理员的角色
        List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("Admin");

        //$2a$10$Jv1lgSebvvdieROLHeZqR.BzuhxwmbnhtVaAe7YaY6kIEIn2YBu8y
        //noop
//        auth.inMemoryAuthentication().withUser("admin").password("{noop}888888").authorities(authorities);

        //$2a$10$Jv1lgSebvvdieROLHeZqR.BzuhxwmbnhtVaAe7YaY6kIEIn2YBu8y
//        String password =  passwordEncoder.encode("666666");
//        System.out.println("password = " + password);
//
//        auth.inMemoryAuthentication().withUser("admin").password(password).authorities(authorities);

        //888888 -->转换成密文


        //设置一个类,用户查询数据库
        auth.userDetailsService(userDetailsService);

    }


    /**
     * 在IOC容器中定义Bean AuthenticationManager
     * @return
     * @throws Exception
     */
    @Bean()
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        //关闭
//        http.csrf().disable();
//        //关闭session策略
//        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//        //允许匿名访问 /user/login
//        http.authorizeRequests().antMatcher("/user/login").anonymous();
//        //其他请求都需要验证
//        http.authorizeRequests().anyRequest().authenticated();

        //上面是普通的写法,因为http对象支持builder模式,所以可以如下写法:
        http.
                //关闭 csrf
                csrf().disable().
                //关闭session策略
                sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
                //允许 /user/login匿名访问
                and().authorizeRequests().antMatcher("/user/login").anonymous().
                //除了以上配置,其他的请求都需要登录
                .anyRequest().authenticated();
        
    }
}

从header中自定义获取jwt

SecurityContext 待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金山老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值