1、什么是SpringSecurity
    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

    2、结合SpringBoot使用步骤

    1. <!-->spring-boot 整合security -->
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-security</artifactId>
    5. </dependency>

    知道HttpBasic模式与FormLogin模式的区别,代码第29行;
    http.authorizeRequests().antMatchers(“/**”).fullyAuthenticated().and().httpBasic();

    1. @Component
    2. @EnableWebSecurity
    3. public class SecurityConfig extends WebSecurityConfigurerAdapter {
    4. @Autowired
    5. private MyAuthenticationFailureHandler failureHandler;
    6. @Autowired
    7. private MyAuthenticationSuccessHandler successHandler;
    8. // 配置认证用户信息和权限
    9. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    10. // 添加admin账号
    11. auth.inMemoryAuthentication().withUser("admin").password("123456").
    12. authorities("showOrder","addOrder","updateOrder","deleteOrder");
    13. // 添加userAdd账号
    14. auth.inMemoryAuthentication().withUser("userAdd").password("123456").authorities("showOrder","addOrder");
    15. // 如果想实现动态账号与数据库关联 在该地方改为查询数据库
    16. }
    17. // 配置拦截请求资源
    18. protected void configure(HttpSecurity http) throws Exception {
    19. // 如何权限控制 给每一个请求路径 分配一个权限名称 让后账号只要关联该名称,就可以有访问权限
    20. http.authorizeRequests()
    21. // 配置查询订单权限
    22. .antMatchers("/showOrder").hasAnyAuthority("showOrder")
    23. .antMatchers("/addOrder").hasAnyAuthority("addOrder")
    24. .antMatchers("/login").permitAll()
    25. .antMatchers("/updateOrder").hasAnyAuthority("updateOrder")
    26. .antMatchers("/deleteOrder").hasAnyAuthority("deleteOrder")
    27. .antMatchers("/**").fullyAuthenticated().and().formLogin().loginPage("/login").
    28. successHandler(successHandler).failureHandler(failureHandler)
    29. .and().csrf().disable();
    30. }
    31. @Bean
    32. public static NoOpPasswordEncoder passwordEncoder() {
    33. return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    34. }
    35. }

    处理登陆是否成功的两个过滤器;再加上上边低30的代码;

    1. //认证失败
    2. @Component
    3. public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    4. public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse res, AuthenticationException auth)
    5. throws IOException, ServletException {
    6. System.out.println("登陆失败!");
    7. res.sendRedirect("http://mayikt.com");
    8. }
    9. }


    1. // 认证成功
    2. @Component
    3. public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    4. public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication arg2)
    5. throws IOException, ServletException {
    6. System.out.println("用户认证成功");
    7. res.sendRedirect("/");
    8. }
    9. }

    更改403报错信息下边两个:

    1. /**
    2. * 自定义 WEB 服务器参数 可以配置默认错误页面
    3. * @version 2018年11月12日
    4. */
    5. @Configuration
    6. public class WebServerAutoConfiguration {
    7. @Bean
    8. public ConfigurableServletWebServerFactory webServerFactory() {
    9. TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    10. ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400");
    11. ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401");
    12. ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403");
    13. ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404");
    14. ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415");
    15. ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");
    16. factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500);
    17. return factory;
    18. }
    19. }


    1. @Controller
    2. public class ErrorController {
    3. // 403权限不足页面
    4. @RequestMapping("/error/403")
    5. public String error() {
    6. return "/error/403";
    7. }
    8. }


    1. @Controller
    2. public class OrderController {
    3. // 首页
    4. @RequestMapping("/")
    5. public String index() {
    6. return "index";
    7. }
    8. // 查询订单
    9. @RequestMapping("/showOrder")
    10. public String showOrder() {
    11. return "showOrder";
    12. }
    13. // 添加订单
    14. @RequestMapping("/addOrder")
    15. public String addOrder() {
    16. return "addOrder";
    17. }
    18. // 修改订单
    19. @RequestMapping("/updateOrder")
    20. public String updateOrder() {
    21. return "updateOrder";
    22. }
    23. // 删除订单
    24. @RequestMapping("/deleteOrder")
    25. public String deleteOrder() {
    26. return "deleteOrder";
    27. }
    28. // 自定义登陆页面
    29. @GetMapping("/login")
    30. public String login() {
    31. return "login";
    32. }
    33. }

    11.12 PPT.pptxspringboot-security.docx上课代码.zip

    3、将SecurityConfig配置成动态的
    先总结一个什么是RBAC权限模型
    就是基于角色的权限访问控制,在五张表的基础上做扩展;用户表,角色表,权限表,用户角色关联表,角色权限关联表;

    1. public interface UserMapper {
    2. // 查询用户信息
    3. @Select(" select * from sys_user where username = #{userName}")
    4. User findByUsername(@Param("userName") String userName);
    5. // 查询用户的权限
    6. @Select(" select permission.* from sys_user user" + " inner join sys_user_role user_role"
    7. + " on user.id = user_role.user_id inner join "
    8. + "sys_role_permission role_permission on user_role.role_id = role_permission.role_id "
    9. + " inner join sys_permission permission on role_permission.perm_id = permission.id where user.username = #{userName};")
    10. List<Permission> findPermissionByUsername(@Param("userName") String userName);
    11. }
    1. // 设置动态用户信息
    2. @Service
    3. public class MyUserDetailsService implements UserDetailsService {
    4. @Autowired
    5. private UserMapper userMapper;
    6. @Override
    7. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    8. // 1.根据用户名称查询数据用户信息
    9. User user = userMapper.findByUsername(username);
    10. // 2.底层会根据数据库查询用户信息,判断密码是否正确
    11. // 3. 给用户设置权限
    12. List<Permission> listPermission = userMapper.findPermissionByUsername(username);
    13. System.out.println("username:" + username + ",对应权限:" + listPermission.toString());
    14. if (listPermission != null && listPermission.size() > 0) {
    15. // 定义用户权限
    16. List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    17. for (Permission permission : listPermission) {
    18. authorities.add(new SimpleGrantedAuthority(permission.getPermTag()));
    19. }
    20. user.setAuthorities(authorities);
    21. }
    22. return user;
    23. }
    24. }


    1. // Security 配置
    2. @Component
    3. @EnableWebSecurity
    4. public class SecurityConfig extends WebSecurityConfigurerAdapter {
    5. @Autowired
    6. private MyAuthenticationFailureHandler failureHandler;
    7. @Autowired
    8. private MyAuthenticationSuccessHandler successHandler;
    9. @Autowired
    10. private MyUserDetailsService myUserDetailsService;
    11. @Autowired
    12. private PermissionMapper permissionMapper;
    13. // 配置认证用户信息和权限
    14. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    15. // // 添加admin账号
    16. // auth.inMemoryAuthentication().withUser("admin").password("123456").
    17. // authorities("showOrder","addOrder","updateOrder","deleteOrder");
    18. // // 添加userAdd账号
    19. // auth.inMemoryAuthentication().withUser("userAdd").password("123456").authorities("showOrder","addOrder");
    20. // 如果想实现动态账号与数据库关联 在该地方改为查询数据库
    21. //如果需要加密则增加passwordEncoder,如果不需要则不用加;
    22. auth.userDetailsService(myUserDetailsService).passwordEncoder(new PasswordEncoder() {
    23. // 加密的密码与数据库密码进行比对CharSequence rawPassword 表单字段 encodedPassword
    24. // 数据库加密字段
    25. public boolean matches(CharSequence rawPassword, String encodedPassword) {
    26. System.out.println("rawPassword:" + rawPassword + ",encodedPassword:" + encodedPassword);
    27. // 返回true 表示认证成功 返回fasle 认证失败
    28. return MD5Util.encode((String) rawPassword).equals(encodedPassword);
    29. }
    30. // 对表单密码进行加密
    31. public String encode(CharSequence rawPassword) {
    32. System.out.println("rawPassword:" + rawPassword);
    33. return MD5Util.encode((String) rawPassword);
    34. }
    35. });
    36. }
    37. // 配置拦截请求资源
    38. protected void configure(HttpSecurity http) throws Exception {
    39. ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = http
    40. .authorizeRequests();
    41. // 1.读取数据库权限列表
    42. List<Permission> listPermission = permissionMapper.findAllPermission();
    43. for (Permission permission : listPermission) {
    44. // 设置权限
    45. authorizeRequests.antMatchers(permission.getUrl()).hasAnyAuthority(permission.getPermTag());
    46. }
    47. authorizeRequests.antMatchers("/login").permitAll().antMatchers("/**").fullyAuthenticated().and().formLogin()
    48. .loginPage("/login").successHandler(successHandler).and().csrf().disable();
    49. }
    50. @Bean
    51. public static NoOpPasswordEncoder passwordEncoder() {
    52. return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    53. }
    54. }

    工具类学习

    public class MD5Util {
    
        // 加盐
        private static final String SALT = "mayikt";
    
        public static String encode(String password) {
            password = password + SALT;
            MessageDigest md5 = null;
            try {
                md5 = MessageDigest.getInstance("MD5");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            char[] charArray = password.toCharArray();
            byte[] byteArray = new byte[charArray.length];
    
            for (int i = 0; i < charArray.length; i++)
                byteArray[i] = (byte) charArray[i];
            byte[] md5Bytes = md5.digest(byteArray);
            StringBuffer hexValue = new StringBuffer();
            for (int i = 0; i < md5Bytes.length; i++) {
                int val = ((int) md5Bytes[i]) & 0xff;
                if (val < 16) {
                    hexValue.append("0");
                }
    
                hexValue.append(Integer.toHexString(val));
            }
            return hexValue.toString();
        }
    
        public static void main(String[] args) {
            System.out.println(MD5Util.encode("123456"));
    
        }
    }
    

    其实用户权限的信息存放在session里的,应用spring-session框架可以做一些优化处理;

    springboot-security.docx上课代码.zip11.12 PPT.pptx