目录
1.1 入门程序
- 因为是安全框架,所以先简单写一个登录的
html
,然后用controller去接收他。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
<!-- 这个必须叫username和password,否则会报错 -->
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
package com.jerry.springsecuritydemo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 登录
*/
@RestController
public class LoginController {
@RequestMapping("/login")
public String login() {
System.out.println("执行了登陆方法");
return "redirect:main:html";
}
}
- 此时我们访问
localhost:8080/login.html
会跳到localhost:8080/login
中,这是springsecurity
带给我们的一个界面,默认的用户名是user
,密码是启动时给的。如下:
登录之后才会到我们自己的登录界面。
这就是最简单的案例。
1.2 自定义登陆逻辑
现在我们的登陆账号和密码都是固定死的,每次由springsecurity
给我们生成,如果我们想要使用自己的数据库中的数据的话,需要以下几点:
- 实现
UserDetailsService
接口并注入到容器中 - 通过重写
loadUserByUsername
即可
loadUserByUsername
方法返回了一个UserDetails
接口,从接口中可以看到它有获取用户名密码权限是否可用等方法。
通过查看UserDetails
的结构可以看到他有一个User
类,我们只需要返回这个实现类即可。
因为在数据库中的密码都是加密过的,所以我们需要一个PasswordEncoder
来进行密码的编码和验证。
由于PasswordEncoder
也是一个接口,并且方法中的注释:
/**
* Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or
* greater hash combined with an 8-byte or greater randomly generated salt.
*/
推荐了我们使用什么方法,我们可以采用他的子类BCryptPasswordEncoder
具体实现:
- SpringSecurity要求Spring容器中必须有一个PasswordEncoder的实例,所以先注入
// 配置类,注入PasswordEncoder
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig {
/**
* SpringSecurity要求Spring容器中必须有一个PasswordEncoder的实例
* @return
*/
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
- 实现UserDetailsService
// 注意@Service注解,或者其余的注解。总之要注入到spring容器中
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
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.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 判断用户名是否存在,不存在要抛出异常UsernameNotFoundException
if (!"admin".equals(username)) {
throw new UsernameNotFoundException("用户名不存在");
}
// 把查询出来的密码(数据库加密过的密码)进行解析,或者直接把密码放入构造方法
String password = passwordEncoder.encode("mypassword");
System.out.println("password:" + password);
return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
}
}
接下来,我们在用原来的user|显示的密码
已经进不去了,反而admin|mypassword
可以进去。
到此我们可以在自定义账号和密码了。
1.3 自定义登陆界面
一般都不会采用springsecurity
的自带的界面。
修改登陆界面需要:
- 使用一个配置去去继承
WebSecurityConfigurerAdapter
重写configure
方法 - 修改登陆界面,登录跳转界面,还有登录路径
- 关闭csrf
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 表单提交
http.formLogin()
.loginProcessingUrl("/login") // 发现/login时认为登录,这个地方必须和login.html提交的那个action一样
.loginPage("/login.html") // 登录的界面
.successForwardUrl("/toMain");
// 其实上面定义了登录界面和成功之后跳转到哪,思考之后发现login方法已经没有用了
// 判断已经有UserDetailsServiceImpl来代替了
http.authorizeRequests() // 类似拦截器
.antMatchers("/login.html").permitAll() // 放行login.html
.anyRequest() // 任何请求
.authenticated(); // 必须都被授权
// 关闭csrf
http.csrf()