目录
简介:
token和jwt的区别
1.快速入门
1.自定义用户名和密码。
自定义重定向。
2.设置权限管理
3.从路径中获取用户信息
2.SpringSecurity核心组件
SecurityContext : authentication对象的容器。
SecurityContextHolder :
Authentication:
编辑
UserDetails: 存储用户信息的。
UserDetailsService:
AuthenticationManager
3.配置类开发
权限判断
角色判断
IP判断(我的版本废除了)
access
4.注解开发(了解)
@secured注解(角色判断)
@PreAuthorize + @PostAuthorize注解
@RemeberMe(前后端分离后,我觉得没必要)
Thymleaf(了解)
CSRF(了解):
总结
springSecuicty是spring的一个安全框架,主要负责处理认证和授权的功能。
认证:认证当前用户是哪个用户,并具体那个用户。(是否登录)
授权:授予某个用户访问权限,并判断他到底是否有访问权限。(有无权限执行:VIP)
其他的安全感框架:apache shiro ,强大且已于上手的安全框架。
授权:貌似底层就想着一个过滤器,Filter从用户的请求判断是否携带相应的请求体来做出相应的相应。
jwt是token的一种复杂实现。
jwt: 全称 json web token 比较复杂,有头部,载荷、签名组成。载荷中,包括用户信息等,并且服务端用秘钥对Jwt进行签名。发给浏览器端,浏览器端,访问时对jwt进行解密,如果解密成功则就是验证成功。
总结 : jwt是一种更复杂的身份令牌信息。
我们基于web项目做测试
1.核心依赖
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-dependencies
${spring-boot.version}
pom
import
2.我们建一个login.html
Title
3.启动springboot项目,访问locahost:8080(你没改端口号的话)
证明security已经成功了。
successForwardUrl(),只能重定向到静态页面。我们点进去发现,
看得出实际上,我们调用successHanler()方法,然后我们url被封装到这里面。
卧槽,发现了什么?请求转发,看来我们url就被封装在这里面,被进行请求转发。
那为什么,全url为什么不能跳转,上述构造器,有一个断言,看url,符不符合格式,否则,那个跳转对象拿到是url是null。
嗯,看来,我们就是要自定义一个类实现AuthenticationSuccessHandler接口,借鉴上述并进行重写。
限制使用。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.
inMemoryAuthentication()
.withUser("qhx2004")
.password("{noop}123456").roles("USER");
}
}
@Configuration
@EnableWebSecurity
// 配置security注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// 设置资源对应的访问角色权限
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.formLogin().and()
.httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.
inMemoryAuthentication()
// 登录账号匹配权限
.withUser("qhx2004").password("{noop}123456").roles("USER").and()
.withUser("admin").password("{noop}111").roles("ADMIN", "USER");
// PassWordEncode
}
}
@RequestMapping("/info")
@ResponseBody
public String productInfo(){
String currentUser = "";
Object principl = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if(principl instanceof UserDetails) {
currentUser = ((UserDetails)principl).getUsername();
}else {
currentUser = principl.toString();
}
return " some product info,用户信息 is: "+currentUser;
}
它包括这很多,诸如用户信息、证书、用户权限、用户身份之类的。看方法,几乎使我们用来获取的,看样子他是别人调用我们拿到的。
public interface Authentication extends Principal, Serializable {
Collection extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal(); // 拿去UserDetail对象的
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
public interface UserDetails extends Serializable {
Collection extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password = "1234";
// 1.我们来自前端的用户名,会调用数据库进行查询,如果成功!
if ("123".equals(username)) {
// 失败 ,抛异常
throw new UsernameNotFoundException("用户并校验错误!");
}
// 成功,返回一个User(UserDetails的实现类,我们的用户信息就封装在这个对象里面)对象
return new User(username, password, AuthorityUtils.createAuthorityList("12"));
}
}
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
用来检验你的Authentication对象是否正确的。
server:
servlet:
context-path: /qhx
// 给项目 + 路径前缀
访问以下资源放行卧槽,反正,我们yml文件整,给项目加了前缀/qhx,看出来区别了。mvc就是专门加了,servletpath
// 授权认证
http.authorizeHttpRequests()
// 放行路径资源
// 一般匹配
.antMatchers("/qhx/login.html").permitAll()
// mvc匹配
.mvcMatchers("/login").servletPath("qhx").permitAll()
// 正则匹配
.regexMatchers("/qhx/register").permitAll()
.anyRequest().authenticated();
// 所有请求都必须被认证,必须登录之后被访问。
内置访问方法解析,
1.测试
return new User(username, password, AuthorityUtils.
// 登陆之后,我给他admin权限
commaSeparatedStringToAuthorityList("admin,ROLE_ADMIN"));
}
2. 资源限定权限访问
.antMatchers("/vip").hasAuthority("admin")
// 放行:admin.admiN访问
.antMatchers("/vip").hasAnyAuthority("admin", "admiN")
.anyRequest().authenticated();
看出来,规定资源能被什么角色访问,登录之后把相应的角色给你,在这里面,也就是说管理员同时具有管理员和普通用户访问资源的权限。
1.测试
return new User(username, password, AuthorityUtils.
// 这个用户能访问普通用户和管理员角色访问的资源
commaSeparatedStringToAuthorityList("qhx,wls,ROLE_ADMIN,ROLE_USER"));
}
2.资源限定角色访问
// 角色管控:限定下面这俩资源,一个管理员访问,另一个普通用户访问
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN", "USER")
.anyRequest().authenticated();
我们打开源码,发现实际上是调用同对象的access方法,同理我们也可直接用access调用。
access还支持自定方法。
public class MyServiceImpl implements MyService {
@Override
public boolean hasProm(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal(); // 拿到user对象
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal; // 强转
Collection extends GrantedAuthority> authorities = userDetails.getAuthorities();
return authorities.contains(new SimpleGrantedAuthority(request.getRequestURI()));
}
return false;
}
}
其实看出来为了迎合发展方向,为了迎合我们的配置,就是静态资源和动态一起过滤,但是呢?随着,前后端分离,但是注解开发也是趋势。
1.启动类加上 EnableGlobalMethodSecurity
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true) // 开启注解启动角色权限(本质上,我们限制的路径资源访问权限)
public class Sspring1securityApplication {
public static void main(String[] args) {
SpringApplication.run(Sspring1securityApplication.class, args);
}
}
2.动态资源加上
@RestController
public class LoginContreoller {
@Secured("ROLE_admin") // 管理员权限能访问
@GetMapping("/login11")
public String login() {
System.out.println("执行登录..");
return "redirect:main.html";
}
}
错误:会报500异常,AccessDeniedException(访问被拒绝错误)
一个在类和方法执行之前权限判断,一个之后判断。
好家伙之前,我们的权限判断和角色控制他都能用,只要输入相应的权限表达式即可。
//@Secured("ROLE_admin") // 管理员权限能访问
@PreAuthorize("hasRole('admin')") // 拥有管理员角色权限才能访问
现在,我们完全不需要记住我,这个关键字,因为默认网站差不多就是一个星期之后需要登录一次,那为什么登陆之后,他怎么判断我们是同一个请求,并且怎么记住一星期呢?
1.通过token来判断的话,我们登录之后,设置一个token被浏览器携带,并在浏览器端计算机硬盘储存一个月。
2.当我们下次登录时,会将token发到服务器端API,如果查询成功,则自动登录。
:模版引擎,能将模版和数据分离,但是又能就和在一起形成特定格式(模版)的产物。
跨域:网络协议 、IP地址 、端口 中任意一个不相同,就是跨域请求。
那么CSRF技术,是通过访问时,同时携带_csrf.token 会与在服务端中的token进行比较,如果成功则允许访问。
SecurityConfig.java
@Configuration
@EnableWebSecurity // 启用security配置
// 自定义配置
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.
userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Override
// 解决静态资源拦截问题(了解)
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/asserts/**");
web.ignoring().antMatchers("/favicon.ico");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 1.配置登录页并允许访问(没用了),下面2个参数对应表单,最后一个跳转页面。
http.formLogin().usernameParameter("username").passwordParameter("password").loginPage("/login");
//2.配置登出页面
http.logout().logoutUrl("/logout").logoutSuccessUrl("/");
//3.给API设置权限
http.authorizeRequests() // 拥有很多权限
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasAnyRole("USER", "ADMIN")
// 任何请求都需要经过认证
.anyRequest().authenticated();
// 4.关闭跨域保护
http.csrf().disable();
}
// 密码加密
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
MyUserDetailsServiceImpl.java
@Configuration
public class MyUserDetailService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 查询就不可能返回boolean值
User user = userMapper.findByUserName(username); // 1.查询数据库
// 2.判断有无用户
if (user == null) {
System.out.println(username + "用户没有注册");
throw new UsernameNotFoundException(username + "用户没有注册");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthority());
}
// 获取权限列表
public List getAuthority() {
//return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
return AuthorityUtils.createAuthorityList("ROLE_ADMIN");
}
}