• Spring Security(1)--- 权限控制基本功能实现


    前言

    Spring Securityacegi进化而来,是一个安全权限管理框架,功能十分的强大。

    但也正是因为功能强大,使用起来就变的非常的麻烦,至少个人感觉很烦很烦,甚至觉得Spring Security是不是应该为常规的Java web应用出一个简化版?相对而言Shiro就清爽很多,当然这里不讨论谁好谁坏,能解决项目的问题就好。

    官方给出的示例中(包括网上一搜就找到的一堆资料)是不使用数据库的,所有的权限配置都写死在配置文件和代码中,这在实际项目中显然是很难满足的,难道老外的权限需求真的如此简单么?

    而想要实现权限的动态可配,友好的提示信息等,这些都需要自己去实现,这实现的过程还是很烦锁的,特别是对Spring Security还不是很熟的情况下。

    目前网上的文章大多都是用xml配置来实现的,本文将全部使用JavaConfig的方式,也不会过多的讲解Spring Security的内容,重在使用,能满足当前项目的需求就好。

    下面以一个最简单的示例开始。

    添加依赖

    maven项目,第一步依然是添加我们需要的依赖。

    在这个示例中,只是简单的演示,并没有涉及到数据库,所以暂时只需要这些。嗯,另外模板引擎换成了thymeleaf,不再是我以前一直使用的velocity了,因为我发现thymeleaf有些地方更好用一些。

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

    系统权限设计

    在本示例中,有以下几个页面,区分不同的权限:

    • 首页 所有人都可以访问
    • 登录页 所有人可都可以访问
    • 欢迎页 登录后的用户都可以访问
    • 管理页 只有管理员可访问
    • 无权限提醒页 当一个用户的访问没有权限时,跳转到该页

    确定了以上页面,接下来就是建立相应的用户了。

    建立用户对象

    为了简单起见,我们的用户只需要用户名、密码以及一个对应的角色。

    public class User {
    
        private String username;
    
        private String password;
    
        private String role;
    
    }

    用户登录数据层

    这里我们并没有真正的数据层,只是建立几个模拟用户数据:

    public class UserDaoImpl implements UserDao {
    
        private static final Map<String, User> userMap = new HashMap<String, User>();
    
        static {
    
            User user = new User();
            user.setUsername("liyd");
            user.setPassword("123456");
            user.setRole("user");
            userMap.put(user.getUsername(), user);
    
            user = new User();
            user.setUsername("admin");
            user.setPassword("123456");
            user.setRole("admin");
            userMap.put(user.getUsername(), user);
        }
    
        @Override
        public User getUser(String username) {
            return userMap.get(username);
        }
    }

    前端展现

    在展现层中,我们需要前面提到的几个页面,并增加一个Controller,代码如下:

    @Controller
    public class UserController {
    
        @RequestMapping(value = { "", "/index" }, method = RequestMethod.GET)
        public String home() {
            return "index";
        }
    
        @RequestMapping(value = "/user-page", method = RequestMethod.GET)
        public String userPage() {
            return "user-page";
        }
    
        @RequestMapping(value = "/admin-page", method = RequestMethod.GET)
        public String adminPage() {
            return "admin-page";
        }
    
        @RequestMapping(value = "/login", method = RequestMethod.GET)
        public String login() {
            return "login";
        }
    
        @RequestMapping("/403")
        public String forbidden() {
            return "403";
        }
    }

    可以看到都是简单的跳转到相应页面,所有的页面都在resources/templates下,这个就不细讲了。

    从上面可以看出/login只是做了一个登录页跳转,但是具体登录的验证逻辑却没有,这是因为Spring Security要求使用者将此块的功能必须委托给它来处理。

    另外/403实际上是用户访问没有权限时跳转的页面,Spring Security会设置此时的http状态码为403,因此我们需要设置一个错误页处理,当发现http状态码为403时跳转到/403处理。

    @Configuration
    public class WebAppConf extends WebMvcConfigurerAdapter {
    
        @Bean
        public EmbeddedServletContainerCustomizer containerCustomizer() {
    
            return new EmbeddedServletContainerCustomizer() {
    
                @Override
                public void customize(ConfigurableEmbeddedServletContainer container) {
    
                    ErrorPage error403Page = new ErrorPage(HttpStatus.FORBIDDEN, "/403");
    
                    container.addErrorPages(error403Page);
                }
            };
        }
    }

    添加权限验证

    到上面那一步,系统功能已经差不多了,但是还缺少权限验证的配置。

    其实权限控制从你向maven的pom.xml中添加spring-boot-starter-security依赖开始就已经起作用了,

    如果这时候你启动项目访问的话,会发现Spring Security已经将所有请求拦截并自动生成了一个登录框让你登录。

    但显然这个登录框你是无法登录成功的,因为后台具体登录的逻辑我们还没有完成。

    建立自定义的UserDetailsService

    Spring Security的用户信息获取最终是通过UserDetailsServiceloadUserByUsername方法来完成的,这个后面会细讲,这里先做了解。

    根据上面的UserDao实现,我们建立自定义的CustomUserDetailService,至于角色的前缀,我记得Spring Security 3.2.x版本是不需要你手动再加的,

    这里我用的是Spring Boot 1.3.3,Spring Security版本为4.0.3,不知道为什么又要加上了,看AffirmativeBased里面的源代码调试,确实是一个有前缀一个没前缀,搞不懂Spring Security走的什么路子。

    public class CustomUserDetailsService implements UserDetailsService {
    
    private static Map<String, User> userMap = new HashMap<String, User>();
    
        static {
            User user = new User("admin", "123456", "admin");
            userMap.put(user.getUsername(), user);
            user = new User("selfly", "123456", "user");
            userMap.put(user.getUsername(), user);
    
        }
    
        @Override
        public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
            User user = userMap.get(s);
            if (user == null) {
                throw new UsernameNotFoundException("not found");
            }
            List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
            authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getRole()));
            LOG.info("username:{},role:{}", user.getUsername(), user.getRole());
            return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                    authorities);
        }
    
    }

    配置Security

    接下来就是配置Spring Security了,我们建立一个类SecurityConf,使用JavaConfig的方式,指定AuthenticationManager使用我们自己的CustomUserDetailsService来获取用户信息,并设置首页、登录页等。

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class SecurityConf extends WebSecurityConfigurerAdapter {
    
        @Bean
        public UserDetailsService userDetailsService() {
            return new CustomUserDetailsService();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService());
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .antMatchers("/", "/index")
                    .permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/user-page")
                    .permitAll()
                    .and()
                    .logout()
                    .permitAll();
        }
    }

    可以看到SecurityConf上添加了@EnableWebSecurity注解用来跟Spring mvc集成。同时它还继承了WebSecurityConfigurerAdapter类用来重写我们需要的配置。

    添加角色权限验证

    上面已经完成了系统的登录和验证功能,但并没有进行权限的区分,要怎么样把普通用户和管理用户区分开呢?

    很简单,只需要增加@PreAuthorize注解即可。修改UserController,对/user/admin分别添加注解:

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    @PreAuthorize("hasAnyRole('admin', 'user')")
    public String userPage() {
        return "user-page";
    }
    
    @RequestMapping(value = "/admin", method = RequestMethod.GET)
    @PreAuthorize("hasAnyRole('admin')")
    public String adminPage() {
        return "admin-page";
    }

    启动项目

    现在,可以启动项目,访问http://localhost:8080,根据提示来登录检查一下权限是否正确。

    当使用admin/123456登录时,所有的页面都是允许访问的。

    当使用selfly/123456登录时,发现访问/admin时跳到了/403页面,提示没有权限,这说明我们的配置是正确的。

  • 相关阅读:
    Delphi 2010下安装IOComp
    为自定义控件的某个属性添加像Winform控件中属性的注释
    C#WinForm仿qq窗体拖到windows窗体边上时,自动隐藏C#WinForm
    给图片添加水印,解决GIF添加水印的问题(无法从带有索引像素格式的图像创建 Graphics 对象)
    怎样把Image数据放入数据库
    取消IE“已限制此网页运行可以访问计算机的脚本
    sql 二进制文件的导入导出
    [转载]数据结构笔试题基础
    [转载]搜索算法(含基本搜索算法与深度搜索与广度搜索算法等思想)
    [转载]面试笔试总结
  • 原文地址:https://www.cnblogs.com/grimm/p/13441687.html
Copyright © 2020-2023  润新知