关于Spring security 5,我踩过的所有坑(基于SpringBoot、Maven、MyBatis、Thymeleaf的整合)

  1. 关于依赖的导入
    在同时使用Spring security 5和Thymeleaf的情况下,除了导入Spring security的基本依赖,还要额外导入整合包的依赖,否则会出现静态资源不加载、js库引入失败等问题
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

		<!--与Thymeleaf整合包-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
  1. 关于自定义页面
    关于自定义页面设置失败的问题,我在我的另一篇博文中进行了详细的讲解,点击跳转:
    Spring security 自定义登录页面,登录后仍然跳转到登录页面 死循环的问题
    另外还要说一个坑,Spring security的登录页面本质上是拦截
    所以通俗的讲,应该是这样的流程
本应走
loginPage
loginProcessingUrl
controller转发到admin/**
登录页面拦截
admin/**
自定义的登录页面

因此,自定义页面

		//自定义登录页面
		//loginPage代表拦截时转发到登录页面controller所接受的路径
		//loginProcessingUrl代表登录表单提交的路径
		//failureForwardUrl代表表单校验失败时controller转发到错误页面所接受的路径
		//usernameParameter、passwordParameter指定前端传入的用户名密码所对应的参数名(默认username、password)
		http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
        
        //自定义注销页面
        //logoutUrl代表注销时controller接受的路径
        //logoutSuccessUrl代表注销成功后controller转发的路径
        http.logout().logoutUrl("/toLogout").logoutSuccessUrl("/");
  1. 关于内存读取与连接数据库两种方式
    前端页面的目录结构
    在这里插入图片描述
  • 从内存中读取信息校验,只需要一个配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;

@EnableWebSecurity
public class LoginConfig extends WebSecurityConfigurerAdapter {

	//授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	//关闭csrf,很重要!!!不然会出现2中所述问题
        http.csrf().disable();
        //设置访问的规则
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/user/**").hasRole("user")
                .antMatchers("/admin/**").hasRole("admin");
		//自定义登录页面        
        http.formLogin().loginPage("/toLogin").failureForwardUrl("/errorLogin").loginProcessingUrl("/login");
        
        //自定义注销页面
        http.logout().logoutUrl("/toLogout").logoutSuccessUrl("/");
    }

	//认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	//使用内存的方式认证admin
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).
        withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("admin");
        
    }
    
	//一些其他的配置
    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
        web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
    }

    @Bean
    public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        firewall.setAllowUrlEncodedDoubleSlash(true);
        return firewall;
    }

}
  • 从数据库中读取信息校验,需要另外自定义CustomUserDetailsService
import com.demo.entity.User;
import com.demo.service.UserService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;


import javax.annotation.Resource;


@Service
public class CustomUserDetailsService implements UserDetailsService {
	
	//注入自己的service,以调用Dao
    @Resource
    private UserService userService;
    
    //参数s代表校验时传入的username
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
    	//使用mybatis依据username查询出实体user
        User user = userService.queryByUserName(s);
        //如果没查到,报错,用户名错误
        if (user == null){
            throw new RuntimeException("用户名错误!");
        }
        //返回Spring security提供的user对象,withUsername配置用户名,password配置加密后的密码,roles配置权限,使用build构建
        return org.springframework.security.core.userdetails.User.withUsername(user.UserName()).password(new BCryptPasswordEncoder().encode(user.getPassword())).roles("user").build();
    }

}

注意!这里有两个大坑要说一下
1. Spring security 5 一定要对密码进行加密,不然会直接报错(可以自定义加密方式,或者使用现成的加密方式,比如上述代码)
2. 看其他博文中,返回值很多都使用的是Spring security User提供的构造方法,传入(用户名,加密的密码,权限集合),但我不知道是不是版本的原因,我使用那种方法时一直权限认证失败,访问页面报403错误,只有使用上述代码中的返回方式才得以验证成功

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;

@EnableWebSecurity
public class LoginConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        //首页所有人都可以访问
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/user/**").hasRole("user")
                .antMatchers("/admin/**").hasRole("admin");
        http.formLogin().loginPage("/toLogin").failureForwardUrl("/errorLogin").loginProcessingUrl("/login");
        http.logout().logoutUrl("/toLogout").logoutSuccessUrl("/");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	//内存读取admin
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("admin");
        //数据库读取user
        auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

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

    @Bean
    public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        firewall.setAllowUrlEncodedDoubleSlash(true);
        return firewall;
    }

}
  1. 关于controller读取当前用户的信息
    @PostMapping("/insert")
    public String insert(Principal principal) {
    	//使用Principal在controller中获得相应的用户信息
		principal.getName();
        return "...";
    }
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值