# springboot-security **Repository Path**: luzhizero/springboot-security ## Basic Information - **Project Name**: springboot-security - **Description**: 基于springboot security的学习资料整理 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-01-09 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spring-security 一、简介 Spring security 能够给speing的企业应用系统提供声明式的安全访问控制解决方法的安全框架。与shiro的作用相同,也分为用户认证(Authentication)和用户授权(Authorization)两个部分。 二、第一个securityDemo 1.idea中创建springboot项目,并添加依赖。 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/162213_62c54b27_5496301.png "屏幕截图.png") 2.创建一个controller ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/162545_c37aa96d_5496301.png "屏幕截图.png") 3.项目启动后控制台会输出一段随机字符串,这就是用户的密码,账户为user ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/162631_f3a387bb_5496301.png "屏幕截图.png") 4.访问http://localhost:8080/login后页面不会返回登录成功而是进入了springsecurity的默认页面 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/162743_b743dc9d_5496301.png "屏幕截图.png") 5.输入用户名和密码进行登录后,页面返回登录成功的信息 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/162820_634a76be_5496301.png "屏幕截图.png") 三、自定义账户和密码 方法1:直接在配置文件中进行配置 ``` spring.security.user.name=jojo spring.security.user.password=123456 ``` 方法2:通过WebSecurityConfigurerAdapter的配置方法进行定义,这里提一下,Security默认采用的加密方式为BCryptpassword,你可以写一个工具类来生成加密后的BCryptpassword的密码,$2a$10$nWRtHgr3jwKc.bWB/WQ.luOgXPVt.Q8bjHSRFKyhPo5r9swoOws0.便是我生成的123456,如果不想使用BCryptpassword可以在passwordEncoder()中对加密方式进行定义。 ``` @Configuration public class securityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //下面这两行配置表示在内存中配置了两个用户 auth.inMemoryAuthentication() .withUser("zhangsan").roles("admin").password("$2a$10$nWRtHgr3jwKc.bWB/WQ.luOgXPVt.Q8bjHSRFKyhPo5r9swoOws0.") .and() .withUser("lisi").roles("user").password("$2a$10$nWRtHgr3jwKc.bWB/WQ.luOgXPVt.Q8bjHSRFKyhPo5r9swoOws0."); } @Bean PasswordEncoder passwordEncoder() { /** * 使用SpringSecurity推荐的BCryptpassword加密类,该加密过程是不可逆的, * 相同的明文每一次加密,加密之后的密文都是不一样的 * * 如果需要其他加密方式,SpringSecurity也提供有如下(在源码类PasswordEncoderFactories中可查看): * encoders.put("ldap", new LdapShaPasswordEncoder()); * encoders.put("MD4", new Md4PasswordEncoder()); * encoders.put("MD5", new MessageDigestPasswordEncoder("MD5")); * encoders.put("noop", NoOpPasswordEncoder.getInstance()); * encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); * encoders.put("scrypt", new SCryptPasswordEncoder()); * encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1")); * encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256")); * encoders.put("sha256", new StandardPasswordEncoder()); * * 加密方法: PasswordEncoder.encode() * 比较方法(判断用户密码输入是否正确): PasswordEncoder.matches(输入未加密的密码,加密的密码) */ return new BCryptPasswordEncoder(); } } ``` 方法3:通过实现UserDetailService来创建登录用户的校验对象,通过对象来进行校验(推荐使用) ``` @Component public class UserDertailServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //写入内存用于和前端数据比较密码. String password = "123456"; //创建一个存储权限数据的集合 Collection authorities = new ArrayList<>(); //给角色进行授权 authorities.add(new SimpleGrantedAuthority("USER")); //返回封装好的用户对象 return new User(username,password,true,true,true,true,authorities); } } ``` 然后在securityConfig 中进行重写configure ``` @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } ``` 四、自定义登录页面 1.创建一个页面,如下 ``` 自定义登录界面
``` 2.在控制层创建一个方法跳转到该页面 ``` @RequestMapping("/login_p") public ModelAndView loginIndex(){ return new ModelAndView("/loginindex.html"); } ``` 3.在配置类重写方法进行配置 ``` @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests()//开启登录配置 .antMatchers("/hello").hasRole("admin")//表示访问 /hello 这个接口,需要具备 admin 这个角色 .anyRequest().authenticated()//表示剩余的其他接口,登录之后就能访问 .and() .formLogin() //定义登录页面,未登录时,访问一个需要登录之后才能访问的接口,会自动跳转到该页面 .loginPage("/login_p") //登录处理接口 .loginProcessingUrl("/doLogin") //定义登录时,用户名的 key,默认为 username .usernameParameter("uname") //定义登录时,用户密码的 key,默认为 password .passwordParameter("passwd") //登录成功的处理器 .successHandler(successHandler) //登录失败的处理器 .failureHandler(failureHandler) //未授权处理器 .permitAll()//和表单登录相关的接口统统都直接通过 .and() .logout() .logoutUrl("/logout") .logoutSuccessHandler(logoutSuccessHandler) .permitAll() .and() //关闭打开的csrf保护 .csrf().disable() //进行权限管理 .exceptionHandling() //无访问权限 .accessDeniedHandler(accessDeniedHandler); } ``` 4.启动后访问 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/163843_9e795a6e_5496301.png "屏幕截图.png") 注:如果在前后端分离的情况下,登录的页面也可以返回json给前端进行处理 ``` @ResponseBody @RequestMapping("/login_p") public String loginIndex(){ return "跳转到登录页面"; } ``` ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/163944_22c54181_5496301.png "屏幕截图.png") 五、自定义逻辑 1.对应登陆结果的处理器 登陆成功处理器(AuthenticationSuccessHandler) ``` @Component public class SuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException { } @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=UTF-8"); PrintWriter writer = httpServletResponse.getWriter(); writer.println("登陆成功"); } } ``` 登陆失败处理器(AuthenticationFailureHandler) ``` @Component public class FailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=UTF-8"); PrintWriter writer = httpServletResponse.getWriter(); writer.println("登录失败"); } } ``` 登出处理器(LogoutSuccessHandler) ``` @Component public class LogoutSuccess implements LogoutSuccessHandler { @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=UTF-8"); PrintWriter writer = httpServletResponse.getWriter(); writer.println("登出成功"); } } ``` 权限不足处理器(AccessDeniedHandler) ``` @Component public class DeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=UTF-8"); httpServletResponse.getWriter().println("没有访问权限"); } } ``` 2.在配置类的对应位置注入这些处理器(于上方第三部相同,这里不做赘述) 3.启动测试 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/164436_d3f4c732_5496301.png "屏幕截图.png") ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/164444_56d16357_5496301.png "屏幕截图.png") ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/164452_a8e98aed_5496301.png "屏幕截图.png") ![输入图片说明](https://images.gitee.com/uploads/images/2020/0109/164457_a5431317_5496301.png "屏幕截图.png") 六、授权和校验 Security的认证逻辑和shiro存在一定的差别,shiro是根据用户查询出用户所有能够访问的资源与访问的资源进行判断,而security是查询出用户的角色,然后和访问的资源所需的角色进行判断. 详细见项目.这里就不展开来说了.