简介:
SpringSecurity是spring家族中的一个安全管理框架,相比与另外一个安全框架shiro,它资源更丰富。
一般中大型项目都是使用SpringSecurity来做安全框架,小项目shiro比较多。
一般web应用的需要进行认证和授权
认证:验证当前访问系统的是不是本系统用户,并且要确认具体是哪个用户
授权:经过认证后判断当前用户是否有权限进行某个操作
而认证和授权是SpringSecurity作为安全框架的核心功能
相关pom
<!--springsecurity相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!--spring启动类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
一、入门体验
新建项目,引入依赖,然后直接启动,打开网址输入 localhost:8080
发现跳转到了默认页面
默认用户名是:admin
密码在启动日志中:
成功登陆啦!
----------这里先告一段落,下面先讲解细的内容,然后再串起来接着上面的例子细化Demo----------------end–
密码解析器详解
这里用到的类是springsecurity提供的PasswordEncoder类
//接口介绍
encode():把参数按照特点的解析规则进行加密
matches():第一个参数是明文密码,第二个是存储的加密后的密码,用于密码比较
(这里提一嘴,这个加密是单向加密的,所以不可能从加密后的密码解析出未被加密的密码)
upgradeEncoding(): 如果加密后的密码能够再次加密且达到更安全的结果则返回true
@Test
void test() {
PasswordEncoder pw = new BCryptPasswordEncoder();
String encode = pw.encode("123456");
System.out.println("加密后的密码"+encode);
//密码对比,正确返回true
boolean matches = pw.matches("123456", encode);
System.out.println(matches);
}
二、UserDetailsService
这个接口是要实现自定义登录逻辑的重要接口,通俗的讲,密码登录校验就在这
登录实现逻辑必须实现该接口
实现逻辑:
1.根据username查询数据库
2.根据查询的对象比较密码
3.返回UserDetail对象
例:简单版本:
-------------------接着入门案例------------------strat
创建一个类,实现UserDetailsService 接口
//写logionservice实现UserDetailsService
@Service
public class LogionServiceImpl implements UserDetailsService {
@Autowired
PasswordEncoder pw;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//这里的用户名和密码是写死的哈,实际应用应该是从库里查出来的
//1.根据username查询数据库
if(!"admin".equalsIgnoreCase(username))
throw new UsernameNotFoundException("用户名或密码错误!");
//2,根据查询的对象比较密码
String password = pw.encode("123456");
//返回用户对象
return new User("admin",password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
}
}
测试 启动项目----->可以发现控制台不再有生成密码,在默认登录页输入 admin 123456进行登录
-------------------入门案例------------------end
例:实际应用版本:
这里把user类改为继承的,验证逻辑改为操作数据库的
//这里可以封装一个User类,这样可以自定义属性,继承security提供的User类
public class FcsUser extends User {
@Getter
private Integer uid;
public FcsUser(Integer uid, String username, String password, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
//这个很重要,是User的构造方法
super(username, password, true, true, true, accountNonLocked, authorities);
this.uid = uid;
}
}
登录逻辑实现
@Slf4j
@Component
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
//user表的dao类,这里你们得自己写啊
private final SysUserRepository sysUserRepository;
//getway限流
private final RequestRateLimiter limiter;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名去查询用户对象
SysUser sysUser = sysUserRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException(username));
//用于限流的,这里可以忽略
boolean lock = limiter.geLimitWhenIncremented(username, 0);
log.info("lock: {}", lock);
return new FcsUser(sysUser.getUserId(), sysUser.getUsername(), sysUser.getPassword(), !lock, new ArrayList<>());
}
}
三、WebSecurityConfigurerAdapter
自定义配置类
例: 自定义一个配置类SecurityConfig
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder pw(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
//配置自定义登录页
.loginPage("/login.html")
//配置这个后,会去找实现了UserDetailsService的接口的loadUserByUsername方法进行登录认证,具体是底层实现
.loginProcessingUrl("/login")
//------------登陆成功/登陆失败后访问接口------------
.successForwardUrl("/toMain")
.failureForwardUrl("/toError")
//----------------------------------------------
//自定义登录用户名/密码 参数(前端的 input name值)
.usernameParameter("username")
.passwordParameter("password");
//授权
http.authorizeRequests()
//放行登录页面
.antMatchers("/login.html").permitAll()
//放行失败页面
.antMatchers("/error.html").permitAll()
//所有请求都必须被认证(登录) 配置这个后,就不能直接去访问未被放行的页面
.anyRequest().authenticated();
//关闭csrf防护
http.csrf().disable();
}
}
controller层
@RequestMapping("/toMain")
public String logion(){
return "redirect:main.html";
}
@RequestMapping("/toError")
public String toError(){
return "redirect:error.html";
}
自定义登录成功逻辑 自定义跳转页面
假如设置登录成功后跳转到www.baidu.com
1.写一个类继承AuthenticationSuccessHandler接口
package com.springsecurity.config;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyAuthenSuccessHandler implements AuthenticationSuccessHandler {
private final String url;
public MyAuthenSuccessHandler(String url) {
this.url = url;
}
//Authentication 这个对象下面会介绍一下
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.sendRedirect(url);
}
}
2.修改上面配置类里的内容
补充:介绍一下Authentication 类
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials(); //获取凭证,也就是密码
Object getDetails(); //当前用户的详情
Object getPrincipal();//其实就是user对象
boolean isAuthenticated();//是否被认证
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException; //设置认证
}
自定义登录失败逻辑
1.写一个类继承AuthenticationFailureHandler接口
//自定义登录成功逻辑
public class MyAuthenFailureHandler implements AuthenticationFailureHandler {
private final String url;
public MyAuthenFailureHandler(String url) {
this.url = url;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.sendRedirect(url);
}
}
2.修改配置类
目录结构如下: