介绍
介绍
开发 REST API 少不了要对资源进行安全保护,SpringBoot 给我们提供了一个很强大的模块 SpringSecurity 来做认证功能的实现
今天我们就来学习如何使用 SpringSecurity 模块实现 HTTP Basic 认证
教程
导入模块
打开 pom.xml,在 dependencies 节点里添加
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
建立认证配置文件
打开 config/SecurityConfig.java,设置如下内容
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() // 设置策略 .anyRequest() // 任何请求 .authenticated() // 都要认证 .and() .httpBasic() // 使用 basic 认证模式 ; } }
使用spring.security.user验证
打开 application.properties,增加如下设置
spring.security.user.name=admin spring.security.user.password=123456
测试
通过浏览器打开任意接口都会弹出需要用户名和密码的认证窗口,然后我们输入配置文件里的 admin/123456 就可以通过认证了
使用inMemoryAuthentication验证
刚刚我们通过 application.properties 设置了用户名和密码实现自动验证
现在我们换一个方法实现
打开 config/SecurityConfig.java,添加如下方法
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("manager") .password("{noop}abcdef") .roles("USER"); }
注意:这里的 {noop} 后面的才是密码,这个 {noop} 是密码加密方式,表示原文密码的意思
支持的加密方式有 noop,bcrypt,pbkdf2,scrypt,sha256 这么几种
必须指定一种密码加密方式,如果不指定任何加密方式,则会报如下错误信息
There is no PasswordEncoder mapped for the id "null"
测试
通过浏览器打开任意接口都会弹出需要用户名和密码的认证窗口,然后我们输入配置文件里的 manager/abcdef 就可以通过认证了
使用userDetailsService验证
前面两种验证方式都只适用于只有一组用户名和密码的情况
下面这种验证方式则适用于类似会员模式的多组用户名和密码的情况
有点复杂,跟我来,别掉队~~
修改安全配置
打开 config/SecurityConfig.java,修改 configureGlobal 方法的内容
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(new AuthUserService()).passwordEncoder(new PasswordEncoder() { @Override public String encode(CharSequence charSequence) { return (String) charSequence; } @Override public boolean matches(CharSequence charSequence, String s) { return s.equals((String) charSequence); } }); }
建立用户详情服务对象
打开 model/AuthUserService.java,设置如下内容
public class AuthUserService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { AuthUser authUser = new AuthUser(s); return authUser; } }
注意:我们的用户详情对象 AuthUser 是通过这里面的用户名 s 作为参数查询出来的
建立用户详情对象
打开 entity/AuthUser.java,设置如下内容
public class AuthUser implements UserDetails { public AuthUser(String s){ } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } @Override public String getPassword() { return "123456"; } @Override public String getUsername() { return "user"; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
注意:构造函数传入了用户名,我们可以根据这个用户名去查询用户信息,然后重写每个属性方法的返回值,为了大家看的清晰这里我没有添加查询数据库之类的逻辑,这个要看明白哦~~
测试
通过浏览器打开任意接口都会弹出需要用户名和密码的认证窗口,然后我们输入配置文件里的 user/123456 就可以通过认证了
总结
如果临时搞一个接口,只给某一个人开放,可以放到 application.properties 里设置,非常简单
如果临时搞一个接口,只给某一个人开放,又需要对密码加密一下,可以通过 inMemoryAuthentication 设置,也很简单
如果是提供自己的会员的功能,需要动态查询用户名和密码,甚至用户状态的话,就需要使用 UserDetailsService 和 UserDetails 来实现了
附录
部分接口免认证的设置方法
在安全配置里这样写
@Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() // 设置策略 .antMatchers("/entity").permitAll() // 这个接口不用认证 .anyRequest() // 任何请求 .authenticated() // 都要认证 .and() .httpBasic() // 使用 basic 认证模式 ; }