前言

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页面,提示没有权限,这说明我们的配置是正确的。

附件列表

你可能感兴趣的内容
Java权限控制算法 收藏,5733 浏览
[译]Gerrit 权限控制 收藏,4283 浏览
76条评论
1290016898 1年前

Spring Security系列一 权限控制基本功能实现

1290016898 1年前

Spring Security系列一 权限控制基本功能实现

1290016898 1年前

Spring Security系列一 权限控制基本功能实现

selfly

交流QQ群:32261424
Owner