1.授权流程分析
授权一定是在认证通过之后,授权流程是通过FilterSecurityInterceptor拦截器来完成,FilterSecurityInterceptor通过调用SecuritymetadataSource来获取当前访问的资源所需要的权限,然后通过调用AccessDecisionManager投票决定当前用户是否有权限访问当前资源。授权流程如下
用户第一次访问是需要加载用户权限以及用户信息的,再访问成功后,在UserService中会返回当前登录成功的对象到SecurityContextHolder并保存,再用户下一次登录时会优先从SecurityContextHolder查询信息。
1.当客户端向某个资源发起请求,请求到达FilterSecurityInterceptor,然后会调用其父类AbstractSecurityInterceptor
的beforeInvocation方法做授权之前的准备工作
2.在beforeInvocation法中通过SecuritymetadataSource…getAttributes(object);获得资源所需要的访问权限 ,通过SecurityContextHolder.getContext().getAuthentication()获取当前认证用户的认证信息,即包含了认证信息和权限信息的Authentication对象
3.然后FilterSecurityInterceptor通过调用AccessDecisionManager.decide(authenticated, object, attributes);进行授权(authenticated中有用户的权限列表,attributes是资源需要的权限),该方法使用投票器投票来决定用户是否有资源访问权限
AccessDecisionManager接口有三个实现类,他们通过通过AccessDecisionVoter投票器完成投票,三种投票策略如下:
Affirmativebased : 只需有一个投票赞成即可通过
Consensusbased:需要大多数投票赞成即可通过,平票可以配置
Unanimousbased:需要所有的投票赞成才能通过
而投票器也有很多,如RoleVoter通过角色投票,如果ConfigAttribute是以“ROLE_”开头的,则将使用RoleVoter进行投票,AuthenticatedVoter 是用来区分匿名用户、通过Remember-Me认证的用户和完全认证的用户(登录后的)
4.投票通过,请求放行,响应对应的资源给客户端
2.Web授权
2.1.web授权API说明
在Security配置类中,可以通过HttpSecurity.authorizeRequests()给资源指定访问的权限,其API如下:
anyRequest():任何请求
antMatchers(“/path”) :匹配某个资源路径
authenticationed() : 保护URL需要登录访问
permitAll():指定url无需保护(放行)一般用户静态资源
hasRole(String role):某个资源需要用户拥有什么样的role才能访问
hasAuthority(String authority):某个资源需要用户拥有什么样的权限才能访问
hasAnyRole(String …roles):某个资源拥有指定角色中的一个就能访问
hasAnyAuthority(String … authorities):某个资源拥有指定权限中的一个就能访问
access(String attribute):该方法使用SPEL表达式,可以创建复杂的限制
hasIpAddress( String ip):拥有什么样的ip或子网可以访问该资源
这里是比较懒了,少创建一张用户角色表,正常创建应该是吧用户表里面的角色id拆分出来,有一张用户和角色关联表
用户表 这里用户密码是通过Security特有的PasswordEncoder加密方式加密,后面会有详细介绍
CREATE TABLE loginuser (
id int(10) NOT NULL AUTO_INCREMENT,
username varchar(255) NOT NULL,
password varchar(255) NOT NULL,
role_id int(10) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
菜单表 菜单表里面存有权限,这个是后面开启注解授权后权限
CREATE TABLE meu (
id int(5) NOT NULL AUTO_INCREMENT,
meuName varchar(255) DEFAULT NULL,
persim varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
角色表
CREATE TABLE role (
id int(10) NOT NULL AUTO_INCREMENT,
roleName varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
角色权限表
CREATE TABLE role_persim (
id int(5) NOT NULL AUTO_INCREMENT,
role_id int(5) NOT NULL,
meu_id int(5) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
这一个配置类就类似于Handlerinterceptor这一个配置类,起到拦截请求的作用。但在这里面有一个强大的密码解析器PasswordEncoder ,用BCrypt强哈希方法来加密,不可逆,此类每次进行加密的盐值均不相同,也就是说每次相同的明文加密后的密文均不相同,这样就能保证密码尽量小的被测出来。
package com.config; import com.securityHandler.MyAuthenticationFailureHandler; import com.securityHandler.MyAuthenticationSuccessHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 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; import org.springframework.stereotype.Component; @Component @EnableWebSecurity // 开启注解授权 @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Bean public PasswordEncoder passwordEncoder() {//密码密码解析器 //return NoOpPasswordEncoder.getInstance(); return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/demo/login").permitAll()//登录请求放行 .antMatchers("/demo/goIndex").permitAll()//访问登录页面放行 .antMatchers("/index.html").permitAll() //对登录页面跳转路径放行 .anyRequest().authenticated() //其他路径都要拦截 .and().formLogin() .passwordParameter("username") .passwordParameter("password")//允许表单登录, 设置登陆页,验证登录账号密码 // .successForwardUrl("/demo/success") // 设置登陆成功后跳转的页面 .defaultSuccessUrl("/demo/success") // 默认登录成功访问指定页面,用户未登入,跳转至登入页面,如果登入成功,跳转至用户访问指定页面,用户访问登入页面,默认的跳转页面 .loginPage("/index.html") //登录页面跳转地址,如果用户没有登录,会跳转到登录页面 .loginProcessingUrl("/demo/login")//登录处理地址(必须)和登录表单请求地址一致,对应后用户登录进入认证流程,赋上对应得权限 //推出登录 .and().logout().permitAll() .logoutUrl("/demo/logout") .logoutSuccessUrl("/demo/goIndex")//和退出登录请求一致 // .deletecookies("JSESSIONID") //退出登录 .invalidateHttpSession(true); //登出后session无效;*/ } }UserDetailsService 用户校验以及权限校验
package com.service.impl; import com.entity.LoginUser; import com.entity.Meu; import com.service.ILoginUserService; import com.service.IMeuService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; 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 java.util.List; import java.util.stream.Collectors; @Configuration public class MyUserDetailService implements UserDetailsService { @Autowired private ILoginUserService loginUserService; @Autowired private IMeuService meuService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { LoginUser loginUser = loginUserService.findByUsername(username); if (loginUser == null){ throw new RuntimeException("此用户不存在,请检查输入的账号是否正确"); } //查询该用户权限 ListHTMLpermission = meuService.selectPermission(loginUser.getId()); List authorities = permission.stream().map(p -> { return new SimpleGrantedAuthority(p.getPersim()); }).collect(Collectors.toList()); //将对象用户密码权限信息先放到holder中返回USER对象 User user1 = new User(username,loginUser.getPassword(),authorities); return user1; } }
//登录页面登陆 登陆
评论列表(0条)