一、前言
上一篇博客记录了仅仅加入spring security依赖,Springboot做的一些自动化配置。这些自动化配置已经可以完成一个完整的登录流程。但是系统不可能只有这一个用户,密码也不能每次启动程序都会改变。所以这篇博客记录一下,在内存中配置用户,以及自定义一些其他的操作。
二、开整
2.1、在配置文件中添加自定义用户
在内存中配置用户的最简单方法就是在配置文件中添加用户
spring:
security:
user:
name: wtq
password: 123456
添加完配置后,再次启动程序会发现控制台不再打印自动生成的密码了,这时候再次访问后端的接口或资源,就可以使用自定义的账户或密码。
2.2、在配置类中添加用户
Springboot中可以在配置文件中配置的组件,在配置类中都可以完成。只需要创建一个配置类SecurityConfig
,继承WebSecurityConfigurerAdapter
类,重写里面的configure(AuthenticationManagerBuilder auth)
方法。这里还为这个角色配置了一个角色roles
,这里先不展开。
如果想要配置多个用户,可以用and()
将每个角色的信息隔开。
/**
* @author wangtianqi
* @create 2020-11-06 10:01
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 提供密码加密方法的bean,这里先不加密
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 在内存中配置用户
auth.inMemoryAuthentication()
.withUser("wtq")
.password("123")
.roles("admin")
.and()
.withUser("lyy")
.password("123")
.roles("user");
}
}
使用第二个用户尝试登陆系统
登陆成功了
2.3、自定义登录表单
如果你感觉Springboot默认的登录表单不太好看,可以配置自己的登录表单。
首先准备一个好看的登录表单。
然后在刚刚的配置类中重写configure(HttpSecurity http)
方法,所有的方法我都添加了注释。
其中.defaultSuccessUrl("/index.html")
这个方法,要单独说一下。
在 Spring Security 中,和登录成功重定向 URL 相关的方法有两个:
- defaultSuccessUrl
- successForwardUrl
这两个咋看没什么区别,实际上内藏乾坤。
首先我们在配置的时候,
defaultSuccessUrl
和successForwardUrl
只需要配置一个即可,具体配置哪个,则要看你的需求,两个的区别如下:
defaultSuccessUrl
有一个重载的方法,我们先说一个参数的defaultSuccessUrl
方法。如果我们在defaultSuccessUrl
中指定登录成功的跳转页面为/index
,此时分两种情况,如果你是直接在浏览器中输入的登录地址,登录成功后,就直接跳转到/index
,如果你是在浏览器中输入了其他地址,例如http://localhost:8080/hello
,结果因为没有登录,又重定向到登录页面,此时登录成功后,就不会来到/index
,而是来到/hello
页面。
defaultSuccessUrl
还有一个重载的方法,第二个参数如果不设置默认为false
,也就是我们上面的的情况,如果手动设置第二个参数为true
,则defaultSuccessUrl
的效果和successForwardUrl
一致。
successForwardUrl
表示不管你是从哪里来的,登录后一律跳转到successForwardUrl
指定的地址。例如successForwardUrl
指定的地址为/index
,你在浏览器地址栏输入http://localhost:8080/hello
,结果因为没有登录,重定向到登录页面,当你登录成功之后,就会服务端跳转到/index
页面;或者你直接就在浏览器输入了登录页面地址,登录成功后也是来到 /index。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
// 配置登录表单
.formLogin()
// 配置登录页
.loginPage("/login.html")
// 配置登录接口Url,默认也是:/doLogin。可以自己定义
.loginProcessingUrl("/doLogin")
// 配置username参数名称(默认为:username)
.usernameParameter("user")
// 配置password参数名称(默认为:password)
.passwordParameter("pass")
.defaultSuccessUrl("/index.html")
// 放行登录相关的页面/接口
.permitAll()
.and()
.csrf().disable();
登录页面中的表单部分
<form action="/doLogin" method="post">
<div class="input">
<label for="name">用户名</label>
<input type="text" name="user" id="name">
<span class="spin"></span>
</div>
<div class="input">
<label for="pass">密码</label>
<input type="password" name="pass" id="pass">
<span class="spin"></span>
</div>
<div class="button login">
<button type="submit">
<span>登录</span>
<i class="fa fa-check"></i>
</button>
</div>
</form>
这时候再次访问登录页面,发现页面样式没有了。因为之前说过,如果不加以配置,spring security会默认保护所有的资源,包括静态资源。所以我们猜测,样式文件被spring security保护起来了。如果是这样,在我们登录完成之后,登录页面的样式就会恢复正常。
登陆一下,果然,像我们猜测的一样,静态资源被拦截了。
这时候只需要在刚刚的配置文件中,重写configure(WebSecurity web)
方法。
@Override
public void configure(WebSecurity web) throws Exception {
// 放行这些路径下的静态资源
web.ignoring().antMatchers("/js/**", "/css/**","/images/**");
}
再次访问登录页面。发现样式也恢复正常了。
2.4、多安全规则配置
如果你的系统中需要多个拦截规则。直接举例说明。下面这两个方法,AdminSecurityConfig
规定了/admin/**
路径下的所有请求都需要已登录的用户拥有admin
角色才可以访问。OtherSecurityConfig
可以做一些其他的规定。
/**
* @author wangtianqi
* @create 2020-11-07 8:42
*/
@Configuration
//@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class MultiHttpSecurityConfig {
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 在内存中配置用户
auth.inMemoryAuthentication()
.withUser("wtq")
.password("123")
.roles("admin")
.and()
.withUser("lyy")
.password("123")
.roles("user")
;
}
@Configuration
@Order(1)
// 配置多个安全规则
public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**")
.authorizeRequests()
.anyRequest()
.hasRole("admin");
}
}
@Configuration
public static class OtherSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.permitAll()
.and()
.csrf().disable();
}
}
}
准备一个简单的接口
@GetMapping("/admin/hello")
public String helloAdmin() {
return "Hello admin!";
}
测试一下,使用admin
账户访问一下,成功访问到了
在使用user
账户访问一下这个页面,失败了,被拦截了。
2.5、权限配置
除了这种路径上的拦截外,还有粒度更小的权限配置。例如对方法的访问权限。实现方法的访问权限也十分简单。
只需要在2.4的代码基础上添加这样一个注解,第一个参数prePostEnabled
表示开启基于方法的安全认证机制,第二个参数securedEnabled
表示开启使用注解限制方法调用。
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class MultiHttpSecurityConfig {
准备测试接口
hello()
方法两个角色都可以访问
helloAdmin()
仅admin用户可以访问
helloUser()
仅user用户可以访问
/**
* @author wangtianqi
* @create 2020-09-17 13:36
*/
@RestController
@RequestMapping("/Hello")
public class HelloSpringBoot {
@GetMapping("/SpringBoot")
@PreAuthorize("hasAnyRole('admin','user')")
public String hello() {
return "Hello SpringBoot!";
}
@GetMapping("/admin")
@PreAuthorize("hasRole('admin')")
public String helloAdmin() {
return "Hello admin!";
}
@PreAuthorize("hasRole('user')")
@GetMapping("/user")
public String helloUser() {
return "Hello user!";
}
测试一下,首先访问一下hello
方法,成功
访问admin
方法,成功
访问user
方法,失败,并且抛出了AccessDeniedException
异常。
user用户就不测试啦。
在内存中配置用户,并且了解一些基础的配置。但是在正常的系统中,用户信息肯定是保存在数据库中的,这些就留在下一篇博客里吧。