如何在SpringBoot项目中引入SpringSecurity进行权限控制

1、前言

  为了方便进行项目开发,自己搭建了一套简单的基于RBAC模型的权限管理系统,其中涉及到的用户、角色、资源(菜单)等模块已经实现了,现在需要引入SpringSecurity来实现认证和授权。

2、引入依赖

  这里使用的SpringSecurity的版本是:5.0.8.RELEASE,SpringBoot版本是:2.0.5.RELEASE。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3、创建UserDetailsService实现类

  UserDetailsService实现类,用于加载用户信息。主要实现了UserDetailsService 接口的loadUserByUsername()方法,根据用户名从数据库查询用户信息和用户权限信息,最后封装成了UserDetails 对象。

@Component("userDetailsService")
public class QriverUserDetailsService implements UserDetailsService {
    
    

    private Logger logger = LoggerFactory.getLogger(QriverUserDetailsService.class);

    @Autowired
    private UserService userService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private RoleService roleService;

    /**
     * 根据username加载数据库中的用户,并构建UserDetails对象。
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
    
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        User param = new User(username);
        User user = userService.querySingle(param);
        if(user == null) {
    
    
            logger.info("用户名不存在,用户名:" + username);
            throw new UsernameNotFoundException("用户名不存在");
        }
        UserRole userRole = new UserRole();
        userRole.setUserId((String) user.getId());
        List<UserRole> userRoles = userRoleService.queryList(userRole);
        for(UserRole uRole : userRoles) {
    
    //保存Role的ID
            authorities.add(new SimpleGrantedAuthority(uRole.getRoleName()));
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),authorities);
    }
}
4、创建PasswordEncoder实现类

  PasswordEncoder实现类,从5.0版本开始强制要求设置,主要用来配置加密方式,这里使用了明文的方式。

@Component("nonPasswordEncoder")
public class QriverNonPasswordEncoder implements PasswordEncoder {
    
    
    @Override
    public String encode(CharSequence rawPassword) {
    
    
        //加密方法可以根据自己的需要修改
        return rawPassword.toString();
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
    
    
        return encode(rawPassword).equals(encodedPassword);
    }
}
5、配置SpringSecurity

  首先,定义SpringSecurityConfig 配置类,该类继承了WebSecurityConfigurerAdapter 抽象类。

  然后,添加注解@Configuration、@EnableWebSecurity,分别表示配置类和开启 Security 服务。

  然后,重写configure(AuthenticationManagerBuilder auth)方法,该方法主要用来配置AuthenticationManager对象相关内容,这里主要配置了自定义的UserDetailsService实现类和PasswordEncoder实现类。

  最后,通过重写configure(HttpSecurity http)方法,进行配置HttpSecurity 相关内容,主要配置了那些URL需要鉴权那些不需要鉴权,自定义登录页面、登录成功或失败时跳转路径、异常拦截等内容。

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    
    

    @Autowired
    private QriverUserDetailsService userDetailsService;

    @Autowired
    private QriverNonPasswordEncoder nonPasswordEncoder;

    /**
     * 配置AuthenticationManager对象
     * @param auth
     * @throws Exception
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        //校验用户
        auth.userDetailsService( userDetailsService )
                .passwordEncoder(nonPasswordEncoder);
    }

    /**
     * 配置 HttpSecurity
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.authorizeRequests()
                //设置不需要鉴权的地址
                .antMatchers("/error","/","index","/login","/login-error","/401","/static/**").permitAll()
                //其他请求均需要鉴权
                .anyRequest().authenticated()
                .and()
                //登录成功跳转页面
                .formLogin().defaultSuccessUrl("/index/toIndex").
                //自定义登录界面
                loginPage( "/login" ).
                //登录失败跳转页面
                failureUrl( "/login-error" )
                .and()
                .exceptionHandling().accessDeniedPage( "/401" )
                .and().csrf().disable()
                .headers().frameOptions().disable();
        http.logout().logoutSuccessUrl( "/" );
    }
}

关于"/login"、"/login-error"、"/index/toIndex"等api接口,这里不再贴出代码了。

6、登录页面

  这里就是一个普通的from表单,最重要的就是from表单的action方法,这里使用了“/login”,且method=“post”,“/login”是SpringSecurity默认的地址,可以在HttpSecurity 中进行配置。

这里需要注意的是,之前自己实现的登录验证,需要首先定义一个“/doLogin”方法,然后登录界面通过from表单或AJAX进行请求,而在SpringSecurity中,则不需要再定义该方法,默认已经实现了,只需要通过HttpSecurity的loginProcessingUrl()方法修改默认的请求的路径地址即可。

//部分代码

<body class="gray-bg">
<div class="middle-box text-center loginscreen  animated fadeInDown">
    <div>
        <div style="margin:25% 0 5% 0;">
            <img src="${ctxPath}/static/img/logo.png"  width="45%;">
        </div>
        <h3>欢迎使用 综合管理平台</h3>

        <form class="m-t" role="form" method="post" action="${ctxPath}/login">
            <div class="form-group">
                <input type="text" id="username" name="username" class="form-control" placeholder="用户名" value="admin" required="">
            </div>
            <div class="form-group">
                <input type="password" id="password" name="password" class="form-control" placeholder="密码" value="123456" required="">
            </div>
            <button type="submit" class="btn btn-primary block full-width m-b">登 录</button>
        </form>
    </div>
</div>
</body>
7、其他

  通过上述步骤,我们基本上已经实现了SpringSecurity的集成,这个时候我们的用户鉴权就会通过SpringSecurity被保护起来了。后续,我们会继续细化一些细节,比如用户名密码错误时,如何更加友好提示?如何登录后,还回到当前页面等功能。

猜你喜欢

转载自blog.csdn.net/hou_ge/article/details/115211781