SpringBoot权限认证

SpringBoot的安全

常用框架:Shrio,SpringSecurity

两个功能:

  • Authentication 认证
  • Authorization 授权

权限:

  • 功能权限
  • 访问权限
  • 菜单权限

原来用拦截器、过滤器来做,代码较多。现在用框架。

SpringSecurity

只要引入就可以使用

可以在官网看教程

几个重要的类:

  • WebSecurityConfigurerAdapter 自定义Security策略
  • AuthenticationManagerBuilder 自定义认证策略
  • @EnableWebSercurity

基本操作

springboot 2.7.0前

继承WebSecurityConfigurerAdapter

重写configure(HttpSecurity http)方法------授权

使用链式编程

第一个小例子:

java 复制代码
http.authorizeRequests()							//自定义权限控制
    .antMatchers("/").permitAll()					//所有人可以访问首页
    .addMatchers("/vip1").hasRole("vip1");			//vip1可以访问
//登录,也可以使用and()拼接
http.fromLogin();									//没有权限会自动跳转到登录页面,即使没有写过/login

源码:默认的login和login?error

重写Configure(AuthenticationManagerBuilder auth)方法------认证

2.7.0版本后,WebSecurityConfigurerAdapter被弃用

上面配置好了什么权限可以做什么事情,这里则用来做登录控制和权限查询操作

java 复制代码
protected void configure(AuthenticationManagerBuilder auth){
    //因为反编译,新版要求所有密码必须加密
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    //JDBC认证	例子为blog_system
    auth.jdbcAuthentication().passwordEncoder(encoder)
                .dataSource(dataSource)
                .usersByUsernameQuery(userSQL)				//通过username、password、enabled登录控制
                .authoritiesByUsernameQuery(authoritySQL);		//通过用户名、权限(在查询的第二列)获取role
    //内存认证	例子来自狂神说课堂笔记,没用数据库的例子
    auth.inMemoryAuthentication().passwordEncoder(encoder)								
        .withUSer("name").password( new BCryptPasswordEncoder("123456")).roles("vip2","vip3")
        .and()	//通过and拼接其他用户
        .withUSer("name2").password(new BCryptPasswordEncoder("123456")).roles("vip2","vip3");
    
    //自定义认证规则,接受Service参数	例子来自VBlog
    auth.userDetailsService(userService);
}

上面的最后一种方式:UserService继承UserDetailService,并重写方法

java 复制代码
@Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(s);
        if (user == null) {
            //避免返回null,这里返回一个不含有任何值的User对象,在后期的密码比对过程中一样会验证失败
            return new User();
        }
        //查询用户的角色信息,并返回存入user中
        List<Role> roles = rolesMapper.getRolesByUid(user.getId());
        user.setRoles(roles);					
        return user;
    }

登录之后若没权限,就是跳转到没权限界面了

基于方法的动态权限

将用户的权限保存在数据库中,并实现动态权限控制。

在配置类上使用@EnableGlobalMethodSecurity来开启它;

然后在方法中使用@PreAuthorize配置访问接口需要的权限;

java 复制代码
@PreAuthorize("hasAuthority('pms:product:create')")

权限字符串自定。

再从数据库中查询出用户所拥有的权限值设置到UserDetails对象中去。

ruoyi项目使用了这种方法

缺点:方法权限写死在代买里了,不好维护。也可以通过过滤器、拦截器实现,

@Since 2.7.0 新用法

新用法非常简单,无需再继承WebSecurityConfigurerAdapter,只需直接声明配置类,再配置一个生成SecurityFilterChainBean的方法,把原来的HttpSecurity配置移动到该方法中即可。

java 复制代码
/**
 * SpringSecurity 5.4.x以上新用法配置
 * 为避免循环依赖,仅用于配置HttpSecurity
 * Created by macro on 2022/5/19.
 */
@Configuration
public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        //省略HttpSecurity的配置
        return httpSecurity.build();
    }

}
Security上下文
java 复制代码
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
注销

前端请求/logout

java 复制代码
http.logout();
//会请求/logout,可以自定义url。默认回到/login?logout
//看源码,可以清空cookies和session
http.logout().logoutUrl("/")	//注销成功回首页
    .csrf().disable()			//关闭防止csrf攻击	csrf:跨站请求攻击,可能屏蔽get。

模板引擎相关功能

页面定制

登录:loginPage(""),改完之后默认的login就没有了

点进去看源码

java 复制代码
/*
.formLogin(formLogin ->
	 * 				formLogin
	 * 					.usernameParameter( username )			//默认
	 * 					.passwordParameter( password )
	 * 					.loginPage( /authentication/login )		登录路由
	 * 					.failureUrl(/authentication/login?failed)
	 * 					.loginProcessingUrl( /authentication/login/process )	登陆验证页面
	 * 			);
*/
"记住我"功能
java 复制代码
http.rememberMe();	//登录页会有"记住我"	保存cookie	默认14天

Shiro

Apache 开源框架

三大对象:

  • Subject 用户
  • SecurityManager 管理用户
  • Realm 连接数据

Subject:主体,代表了当前 "用户",与当前应用交互的任何东西都是 Subject,如网络爬虫,人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

Realm:域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

也就是说对于我们而言,最简单的一个 Shiro 应用:

  1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;
  2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。
xml 复制代码
    <!--shiro依赖- SSM-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.2.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>1.2.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.2.4</version>
    </dependency>

shiro-web提供了Web集成的支持,其通过一个 ShiroFilter 入口来拦截需要安全控制的 URL,然后进行相应的控制

1.编写配置类

java 复制代码
@Configuration
public class ShiroConfig{
    //ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean hetShiroFilterFactoryBean(@Qualifier DefaultWebSercurityManager defaultWebSercurityManager){
 	ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defau+ltWebSercurityManager);
        Map<String,String> filterMap = new LinkedHashMap<>();
        
        filterMap.put("","authc")
        return bean;
    }
    
    //DefaultWebSercurityManager
    @Bean
    public DefaultWebSercurityManager getDefaultWebSercurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSercurityManager sercurityManager new DefaultWebSercurityManager();
        securityManager.setRealm(userRealm);
        return sercurityManager;
    }
    
    //Realm	自定义
    @Bean
    public Realm userRealm(){
        return new UserRealm();
    }
}
java 复制代码
public UserRealm extends AuthorizingRealm{
    
   	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 用户名
		String username = (String) token.getPrincipal();
		// 密码
		String password = new String((char[]) token.getCredentials());
		Userlogin userlogin = null;
		try {
			userlogin = userloginService.findByName(username);
		} catch (Exception e) {
			e.printStackTrace();
		}
        
		if (userlogin == null) {
			// 没有该用户名
			throw new UnknownAccountException();
		} else if (!password.equals(userlogin.getPassword())) {
			// 密码错误
			throw new IncorrectCredentialsException();
		}
		// 身份验证通过,返回一个身份信息
		AuthenticationInfo aInfo = new SimpleAuthenticationInfo(username, password, getName());

		return aInfo;
	}
}

2.登录控制

java 复制代码
//登录表单处理
    @RequestMapping(value = "/login", method = {RequestMethod.POST})
    public String login(Userlogin userlogin) throws Exception {

        //Shiro实现登录
        UsernamePasswordToken token = new UsernamePasswordToken(userlogin.getUsername(),
                userlogin.getPassword());
        Subject subject = SecurityUtils.getSubject();

        //如果获取不到用户名就是登录失败,但登录失败的话,会直接抛出异常
        //登录
        subject.login(token);

        if (subject.hasRole("admin")) {
            return "redirect:/admin/showStudent";
        } else if (subject.hasRole("teacher")) {
            return "redirect:/teacher/showCourse";
        } else if (subject.hasRole("student")) {
            return "redirect:/student/showCourse";
        }

        return "/login";
    }
相关推荐
嘟嘟MD4 小时前
程序员副业 | 2025年12月复盘
后端·创业
利刃大大5 小时前
【SpringBoot】Spring事务 && @Transactional详解 && Spring事务失效问题
spring boot·spring·事务
..过云雨6 小时前
17-2.【Linux系统编程】线程同步详解 - 条件变量的理解及应用
linux·c++·人工智能·后端
廋到被风吹走6 小时前
【数据库】【Oracle】分区表与大表设计
数据库·oracle
南山乐只7 小时前
【Spring AI 开发指南】ChatClient 基础、原理与实战案例
人工智能·后端·spring ai
㳺三才人子7 小时前
初探 Spring Framework OncePerRequestFilter
spring boot·spring·junit
这是程序猿8 小时前
基于java的ssm框架学生作业管理系统
java·开发语言·spring boot·spring·学生作业管理系统
努力的小雨8 小时前
从“Agent 元年”到 AI IDE 元年——2025 我与 Vibe Coding 的那些事儿
后端·程序员
源码获取_wx:Fegn08958 小时前
基于springboot + vue小区人脸识别门禁系统
java·开发语言·vue.js·spring boot·后端·spring
wuxuanok9 小时前
Go——Swagger API文档访问500
开发语言·后端·golang