注:本篇写作思维旨在引导初学security的码友,一步步的达到可以灵活使用的地步,建议各位读者阅读前,先把我的源代码下载下来,对着master分支看比较好理解,磨刀不误砍柴工,https://github.com/1074952839/spring-security-demo01.git
springsecurity的引入(springboot版本为:2.1.3)
我们先准备一下如下的代码:
新建springboot项目,命名为:springboot-security-oauth2(之所以这么命名,是为了后期扩展项目达到企业级开发的目的)
然后我们建一个controller包,并在其中建一个UserController,内容如下:
1
2
3
4
5
6
7
8
9
10
11/**
* Created by xk on 2018/11/26
*/
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping
public String get(){
return "看到我了吗?";
}
}然后我们启动服务,访问localhost:8080/user,看到如下效果:
那问题来了,我想让这个接口受到保护,也就是需要用户登录后,才能访问这个接口怎么办呢?那好办呀,我们引入springsecurity试试呀!继续往下看吧。
我们继续接着上面的例子往下走,我们在pom.xml里面加入如下2个依赖:
1 | <dependency> |
然后我们再重新启动项目,访问localhost:8090/user,看到如下效果:
哎呀,居然直接跳到localhost:8090/login地址了,然后出现这个表单了,这是因为springsecurity的默认配置。
那我们怎么才能访问到localhost:8090/user接口呢?
直捣黄龙吧,security默认的用户名为:user,密码可以在项目启动时发现,如图:
填写完成后,就可以访问到localhost:8090/user接口了。
那么问题来了,我想自己做个注册功能,然后用我自己注册的用户名和密码登录咋办呢?
我们在MySQL的security-demo库里建一个user表,表结构如下:
UserController里新增insert方法:
1
2
3
4
5
6
7@Autowired
private UserRepository userRepository; //本文档使用的是spring-data-jpa做数据访问框架
@GetMapping("/register")//这么写其实不符合restful规范,这里为了好写demo,暂且这么写。
public ResponseEntity<ResponseData> insert(User user){
return ResponseEntity.ok(new ResponseData(userRepository.save(user)));//ResponseData这个类是我自己写的类
}
重新启动项目,我们发送localhost:8090/user/register?name=test&password=test请求来注册一个用户,我们发现这时,请求并没有到UserController里,而是直接给我们返回一个登陆页面了!!!
上面的问题,其实不难发现,因为我们引入了spring-security,我们前面已经说了,他会默认的对所有请求做拦截,需要登录才能请求相关接口,而实际情况下,我们不应该拦截用户注册的请求呀?所以继续看如下神操作:
我们新建一个config包,然后新建WebSecurityConfig类,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* Created by xk on 2018/11/26
*/
@Component
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.authorizeRequests()
.antMatchers("/user/register").permitAll()//这里我们允许/user/register请求可以直接访问,其它请求都需要认证。
.anyRequest()
.authenticated();
}然后我们继续发送localhost:8090/user/register?name=test&password=test请求来注册一个用户,可以发现浏览器给我们返回了用户的信息,并且数据库已经有记录了。
接下来,我们继续访问localhost:8090/user吧,结果又跳到了登陆页面,那我们拿test,test登陆试试,结果悲剧了。。。。。。。。
咋还登陆不上去呢???
接下来,继续神操作,我们再config包里新建UserService类,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13@Component
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
top.xiekun.springbootsecurityoauth2.domain.User user = userRepository.findByName(name);
if (user != null){
return new User(user.getName(), user.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));//这段话,后面的分享会讲解的。
}
return null;
}
}我们继续访问localhost:8090/user吧,结果又跳到了登陆页面,那我们拿test,test登陆试试,结果又悲剧了,还是登陆不上去呀!我们先看一下下图:
其实通过调试我们发现,springsecurity在做密码比对时,会先查看这个项目有木有配置PasswordEncoder,若没有的话就会抛出一个上图所示异常,所以才登陆不成功。
了解了问题的原因,那接下来我们将代码进行如下修改:
WebSecurityConfig类添加如下内容:
1
2
3
4@Bean
private PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}UserController类修改为如下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* Created by xk on 2018/11/26
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@GetMapping
public List<User> get(){
return userRepository.findAll();
}
@GetMapping("/register")
public ResponseEntity<ResponseData> insert(User user){
user.setPassword(passwordEncoder.encode(user.getPassword()));
return ResponseEntity.ok(new ResponseData(userRepository.save(user)));
}
}接下来我们发送localhost:8090/user/register?name=xiekun&password=123重新注册个用户,可以看到数据库多了一条记录,我们用新注册的用户名,密码发现登陆可以成功了。此时访问localhost:8090/user,发现可以成功查询到所有的用户了。