前端的页面中,一些特定的接口需要一定的条件才能进行访问(比如说图书管理员可以访问图书管理的接口)
即很多接口需要一定的条件或权限才能访问,所以需要用到SpringSecurity
访问接口之前需要经过大量的过滤器(身份过滤、权限过滤、安全过滤等等)
一:SpringSecurity的使用
新建一个Module——选择Spring Initializr ——java版本选择8——NEXT——勾选以下五个组件——Finish
1:设置好各种层级
然后新建一个controller
其中三个需要拟实现的接口,分别是
- 不需要登录即可进行访问的接口
- 需要登录才可以访问的接口
- 需要登录后,并且具有一定的身份(权限)才可以访问的接口
package com.example.springsecurity.controller;
import com.example.springsecurity.utils.Result;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin
@RestController
public class TestController {
@GetMapping("home")
public Result home(){
return Result.ok("这是不需要登录的接口");
}
@GetMapping("test")
public Result test(){
return Result.ok("这是需要登录的接口");
}
@GetMapping("admin")
public Result admin(){
return Result.ok("需要登录,并且具有管理员权限才能进行访问的接口");
}
}
2:新建工具类
MyWebSecurityConfigurerAdapter
package com.example.springsecurity.utils;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
//该注解的作用是程序启动时该类会被加载
@EnableWebSecurity
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
// super.configure(auth);
// Password Encoder 需要对密码进行加密
auth.inMemoryAuthentication()
.withUser("user")
.password(new BCryptPasswordEncoder().encode("123456"))
.authorities("user");
}
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests() //所有的方法都检查
.antMatchers("/home","/user/**").permitAll()//这个方法不拦截
.antMatchers("/admin").hasAuthority("admin")//需要权限才能访问
.anyRequest().authenticated(); //其他的方法还是拦截
http.formLogin();//将拦截的方法引导至登录接口
}
}
对上述代码进行解析
到这一步完成,运行后可以访问下面三个端口进行测试
http://localhost:8080/home 这个端口可以访问,而且不需要登录
http://localhost:8080/test 这个端口访问会跳转登录界面
http://localhost:8080/admin 这个端口访问会跳转登录界面
登录界面都是SpringSecurity封装好的。
尝试进行登录
用户名 user 密码 123456
登录成功之后跳转到这个页面是正常现象。是因为没有设置跳转页面而已。
之后再次访问上述端口即可发现测试数据。
测试的时候可以访问 http://localhost:8080/logout 端口退出登录,这也是SpringSecurity封装好的。
如果将赋予权限部分
.authorities("user","admin");
修改为
.authorities("user");
即不赋予登录用户admin权限。
那么登录的时候就无法访问 http://localhost:8080/admin 端口。
因为权限不是admin被拒绝。
3:上述设置中有一些问题——用户不是动态的
用户名强制设置为user 密码总是123456
如何设置为动态的呢。
注释替换MyWebSecurityConfigurerAdapter中的代码为后边代码
// protected void configure(AuthenticationManagerBuilder auth) throws Exception{
super.configure(auth);
Password Encoder 需要对密码进行加密
// auth.inMemoryAuthentication()
// .withUser("user")
// .password(new BCryptPasswordEncoder().encode("123456"))
// .authorities("user","admin");
// }
protected void configure(AuthenticationManagerBuilder auth)throws Exception{
auth.userDetailsService(myUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
然后新建一个工具类MyUserDetailsService
package com.example.springsecurity.utils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service("MyUserDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// username 登录框返回的用户名
// 然后需要去数据库中查询对应的账号密码 此处省略查询步骤,假设查询到了密码为password
String password = "123456";
// 确认用户名存在,并且用户名对应的密码和密码框的值对应一致的情况下再进行以下处理,这里省略了以上步骤
List<GrantedAuthority> authorityList = new ArrayList<>();
// 去数据库中查询用户所拥有的权限 并将其绑定 这里绑定了权限设置为user
GrantedAuthority user = new SimpleGrantedAuthority("user");
authorityList.add(user);
return new User(username,new BCryptPasswordEncoder().encode(password),authorityList);
}
}
这样就可以和数据库相关联。(具体连接数据库部分省略,此处只是框架样式)
4:再进一步分析上述项目的问题——没有达到前后端分离的效果
认证的html页面其实是后端生成的,没有达到前后端分离的效果。
将拦截跳转进行修改即可
修改MyWebSecurityConfigurerAdapter.java
将http.formLogin();进行修改
在controller层再加一个接口,用于返回需求登录认证
@GetMapping("mylogin")
public Result mylogin(){
return Result.error("请登录");
}
实际案例如:前端进入某些需要登录认证的页面,然后前端请求后端mylogin端口,得到一些状态码,然后跳转前端的登陆页面,将登陆的用户名密码(username、password)一起发给后端的login接口,这个接口是不需要我们写的,SpringSecurity提供。
大致的流程基本如上,但是对于login接口还是存在有一些其他问题。
比如说,返回的数据不是一般的JSON数据,而是页面。
并且这样进行前后端处理对于登录的安全性会有一些影响,浏览器如何保存Cookie也是个问题。
可以自行去了解。