Spring Security是 一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和受权。html
有关认证和受权的理论知识,以前有写过相关博客。了解权限管理java
网上找一张图,以为画的挺好的,比较容易理解。否则换的是源码流程图很难去理解。git
图片地址 : 地址 能够单机放大看更加清楚github
要想理解这张图建议看下这篇博客,由于这张图中须要自定义的My...类,在文章中都有说明,因此更好理解点。web
Spring Boot Security 详解spring
这里只展现一些核心代码,具体完整代码放在github上。数据库
Security 中的用户接口,咱们自定义用户类要实现该接口, 用于向security中注入当前用户的姓名密码,和拥有的角色。同时也包含一些其它信息,好比当前用户是否过时,安全
帐号是否锁定等等。app
本身定义User实现这个接口框架
public class User implements UserDetails { private String username; private String password; private List<Role> roles; /** * 获取用户名 */ @Override public String getUsername() { return username; } /** * 获取密码 */ @Override public String getPassword() { return password; } /** * 用户的权限集, 默认须要添加ROLE_ 前缀 */ @Override @JsonIgnore public List<GrantedAuthority> getAuthorities() { List<GrantedAuthority> authorities = new ArrayList<>(); for (Role role : roles) { authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())); } return authorities; } /** * 帐户是否过时 */ @Override @JsonIgnore public boolean isAccountNonExpired() { return true; } /** * 帐户是否锁定 */ @Override @JsonIgnore public boolean isAccountNonLocked() { return true; } /** * 凭证是否过时 */ @Override @JsonIgnore public boolean isCredentialsNonExpired() { return true; } /** * 用户是否可用 */ @Override public boolean isEnabled() { return true; } }
Security 中的用户 Service,自定义用户服务类须要实现该接口。这个接口只有一个方法须要咱们去实现,那就是经过用户名去获取用户信息。这里也是和数据库交互获取
用户认证和受权信息的地方。
@Service @Slf4j public class UserService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { //TODO 正常应该查询数据库获取用户和用户的权限 // User user = userMapper.loadUserByUsername(userName); // List<Role> roles = rolesMapper.getRolesByUid(user.getId()); // user.setRoles(roles); log.info("登录用户名: {}", userName); //经过用户名查询到的密码 密码确定是加密过的 这里明文密码是 123456 String password = "e10adc3949ba59abbe56e057f20f883e"; //用户对应权限 List<Role> roles = Lists.newArrayList(new Role(1L, "教师"), new Role(2L, "学生")); User user = new User(userName, password, roles); return user; } }
注意
这里的明文密码是 123456,也就是用户输入这个才能完成认证。受权的话当前用户有两个角色 教师
和 学生
。在下面测试的时候会用到。
它是Spring Security的Java 配置类。建立类SecurityConfiguration继承 WebSecurityConfigurerAdapter
,来对咱们应用中全部的安全相关的事项(
全部url,验证用户名密码,表单重定向等)进行控制。
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * 一、配置的是认证信息, AuthenticationManagerBuilder 这个类,就是AuthenticationManager的建造者, 咱们只须要向这个类中, 配置用户信息, * 就能生成对应的AuthenticationManager, 这个类也提过,是用户身份的管理者, 是认证的入口, 所以,咱们须要经过这个配置,想security提供真实的用户身份。 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { } /** * 二、配置Security的认证策略, 每一个模块配置使用and结尾。这个也是最复杂的 */ @Override protected void configure(HttpSecurity http) throws Exception { } /** * 三、这个配置方法用于配置静态资源的处理方式,可以使用 Ant 匹配规则。就是能够不用认证就能够直接访问的接口 */ @Override public void configure(WebSecurity web) throws Exception { } }
完整示例
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; /** * 密码验证器 */ @Autowired private PassWordEncorder passWordEncorder; /** * 成功处理器 */ @Autowired private AuthenctiationSuccessHandler authenctiationSuccessHandler; /** * 失败处理器 */ @Autowired private AuthenctiationFailHandler authenctiationFailHandler; /** * 向Security注入用户信息 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passWordEncorder); } /** * 配置规则 */ @Override protected void configure(HttpSecurity http) throws Exception { //开启登录配置 http.authorizeRequests() // 登陆以后就能访问 .antMatchers("/no-authorize").authenticated() // 登录后 须要校长角色权限 .antMatchers("/need-authorize").hasRole("校长") // 其余的路径都是登陆后便可访问 .anyRequest().authenticated() .and().formLogin() // 定义登陆页面,未登陆时,访问一个须要登陆以后才能访问的接口,会自动跳转到该页面 .loginPage("/login_page") //登陆成功的处理器 .successHandler(authenctiationSuccessHandler) //登陆失败的处理器 .failureHandler(authenctiationFailHandler) // 登陆处理接口 .loginProcessingUrl("/login") // 定义登陆时,用户名的 key,默认为 username .usernameParameter("username") //定义登陆时,用户密码的 key,默认为 password .passwordParameter("password").permitAll() .and().logout() ////和表单登陆相关的接口通通都直接经过 .permitAll() .and().csrf().disable().exceptionHandling().accessDeniedHandler(getAccessDeniedHandler()); } /** * 对于/static/ 下的路径都不用认证 */ @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/no-login"); } /** * 用户未认证异常拦截 */ @Bean AccessDeniedHandler getAccessDeniedHandler() { return new AuthenticationAccessDeniedHandler(); } }
注意
这里一共配置了三个路径用于测试。
一、/no-login 接口不须要认证就能够直接访问 二、/no-authorize 须要认证 但不须要受权就能够访问 三、/need-authorize 首先须要认证 认证经过还须要受权 这里须要校长的角色才能够访问该接口 可是咱们测试用户只有教师和学生因此没有权限访问该接口
下面会针对这个个接口分别进行测试。
@RestController public class TestController { /** * 一、不须要登录就能够访问 */ @RequestMapping(value = "/no-login") public ServiceResponse noLogin() { return ServiceResponse.success("欢迎访问不须要登录接口"); } /** * 二、只登录,不准认证接口 */ @RequestMapping(value = "/no-authorize") public ServiceResponse needAuthorize(){ return ServiceResponse.success("登录了 不用受权"); } /** * 三、登录 + 相关认证接口 */ @RequestMapping(value = "/need-authorize") public ServiceResponse noAuthorize() { return ServiceResponse.success("登录+受权成功"); } /** * @Description: 若是自动跳转到这个页面,说明用户未登陆,返回相应的提示便可 */ @RequestMapping("/login_page") public ServiceResponse loginPage() { return ServiceResponse.failure("001", "还没有登陆,请登陆!"); } }
no-login
接口
很明显没有登录 请求该接口成功!
no-authorize
接口
没有登录访问失败,在上面配置了若是用户没有认证的话跳转到login_page接口,因此这里返回 '还没有登陆,请登陆!'
先登录
根据上面配置登录的路径为 /login 请求参数包括 username 和 password
注意
这里须要post请求。
no-authorize
接口
登录就能够访问了。
need-authorize
接口
虽然登录成功了,可是由于该接口须要校长角色,以前给该用户只配置了教师和学生的角色因此访问失败。
别人骂我胖,我会生气,由于我内心认可了我胖。别人说我矮,我就会以为可笑,由于我内心知道我不可能矮。这就是咱们为何会对别人的攻击生气。 攻我盾者,乃我心里之矛(17)