首先,引入依赖:

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

引入此依赖之后,你的web程序将拥有以下功能:

  • 所有请求路径都需要认证
  • 不需要特定的角色和权限
  • 没有登录页面,使用HTTP基本身份认证
  • 只有一个用户,名称为user

配置SpringSecurity

springsecurity配置项,最好保存在一个单独的配置类中:

  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  4. }

**配置用户认证方式**

首先,要解决的就是用户注册,保存用户的信息。springsecurity提供四种存储用户的方式:

  • 基于内存(生产肯定不使用)
  • 基于JDBC
  • 基于LDAP
  • 用户自定义(最常用)

使用其中任意一种方式,需要覆盖configure(AuthenticationManagerBuilder auth)方法:

  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  4. @Override
  5. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  6. }
  7. }

1.基于内存

  1. @Override
  2. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  3. auth.inMemoryAuthentication()
  4. .withUser("zhangsan").password("123").authorities("ROLE_USER")
  5. .and()
  6. .withUser("lisi").password("456").authorities("ROLE_USER");
  7. }

2.基于JDBC

  1. @Autowired
  2. DataSource dataSource;
  3. @Override
  4. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  5. auth.jdbcAuthentication()
  6. .dataSource(dataSource);
  7. }

基于JDBC的方式,你必须有一些特定表表,而且字段满足其查询规则:

  1. public static final String DEF_USERS_BY_USERNAME_QUERY =
  2. "select username,password,enabled " +
  3. "from users " +
  4. "where username = ?";
  5. public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
  6. "select username,authority " +
  7. "from authorities " +
  8. "where username = ?";
  9. public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
  10. "select g.id, g.group_name, ga.authority " +
  11. "from groups g, group_members gm, group_authorities ga " +
  12. "where gm.username = ? " +
  13. "and g.id = ga.group_id " +
  14. "and g.id = gm.group_id";

当然,你可以对这些语句进行一下修改:

  1. @Override
  2. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  3. auth.jdbcAuthentication().dataSource(dataSource)
  4. .usersByUsernameQuery("select username, password, enabled from Users " +
  5. "where username=?")
  6. .authoritiesByUsernameQuery("select username, authority from UserAuthorities " +
  7. "where username=?");

这有一个问题,你数据库中的密码可能是一种加密方式加密过的,而用户传递的是明文,比较的时候需要进行加密处理,springsecurity也提供了相应的功能:

  1. @Override
  2. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  3. auth.jdbcAuthentication().dataSource(dataSource)
  4. .usersByUsernameQuery("select username, password, enabled from Users " +
  5. "where username=?")
  6. .authoritiesByUsernameQuery("select username, authority from UserAuthorities " +
  7. "where username=?")
  8. .passwordEncoder(new StandardPasswordEncoder("53cr3t");

passwordEncoder方法传递的是PasswordEncoder接口的实现,其默认提供了一些实现,如果都不满足,你可以实现这个接口:

  • BCryptPasswordEncoder

  • NoOpPasswordEncoder

  • Pbkdf2PasswordEncoder

  • SCryptPasswordEncoder

  • StandardPasswordEncoder(SHA-256)

3.基于LDAP

  1. @Override
  2. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  3. auth.ldapAuthentication()
  4. .userSearchBase("ou=people")
  5. .userSearchFilter("(uid={0})")
  6. .groupSearchBase("ou=groups")
  7. .groupSearchFilter("member={0}")
  8. .passwordCompare()
  9. .passwordEncoder(new BCryptPasswordEncoder())
  10. .passwordAttribute("passcode")
  11. .contextSource()
  12. .root("dc=tacocloud,dc=com")
  13. .ldif("classpath:users.ldif");

4.用户自定义方式(最常用)

首先,你需要一个用户实体类,它实现UserDetails 接口,实现这个接口的目的是为框架提供更多的信息,你可以把它看作框架使用的实体类:

  1. @Data
  2. public class User implements UserDetails {
  3. private Long id;
  4. private String username;
  5. private String password;
  6. private String fullname;
  7. private String city;
  8. private String phoneNumber;
  9. @Override
  10. public Collection<? extends GrantedAuthority> getAuthorities() {
  11. return null;
  12. }
  13. @Override
  14. public boolean isAccountNonExpired() {
  15. return false;
  16. }
  17. @Override
  18. public boolean isAccountNonLocked() {
  19. return false;
  20. }
  21. @Override
  22. public boolean isCredentialsNonExpired() {
  23. return false;
  24. }
  25. @Override
  26. public boolean isEnabled() {
  27. return false;
  28. }
  29. }

有了实体类,你还需要Service逻辑层,springsecurity提供了UserDetailsService 接口,见名知意,你只要通过loadUserByUsername返回一个UserDetails对象就成,无论是基于文件、基于数据库、还是基于LDAP,剩下的对比判断交个框架完成:

  1. @Service
  2. public class UserService implements UserDetailsService {
  3. @Override
  4. public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  5. return null;
  6. }
  7. }

最后,进行应用:

  1. @Autowired
  2. private UserDetailsService userDetailsService;
  3. @Bean
  4. public PasswordEncoder encoder() {
  5. return new StandardPasswordEncoder("53cr3t");
  6. }
  7. @Override
  8. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  9. auth.userDetailsService(userDetailsService)
  10. .passwordEncoder(encoder());
  11. }

**配置认证路径**

知道了如何认证,但现在有几个问题,比如,用户登录页面就不需要认证,可以用configure(HttpSecurity http)对认证路径进行配置:

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. }

你可以通过这个方法,实现以下功能:

  • 在提供接口服务前,判断请求必须满足某些条件
  • 配置登录页面
  • 允许用户注销登录
  • 跨站点伪造请求防护

1.保护请求

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.authorizeRequests()
  4. .antMatchers("/design", "/orders").hasRole("ROLE_USER")
  5. .antMatchers(“/”, "/**").permitAll();
  6. }

要注意其顺序,除了hasRolepermitAll还有其它访问认证方法:

方法 作用
access(String) 如果给定的SpEL表达式的计算结果为true,则允许访问
anonymous() 允许访问匿名用户
authenticated() 允许访问经过身份验证的用户
denyAll() 无条件拒绝访问
fullyAuthenticated() 如果用户完全通过身份验证,则允许访问
hasAnyAuthority(String...) 如果用户具有任何给定权限,则允许访问
hasAnyRole(String...) 如果用户具有任何给定角色,则允许访问
hasAuthority(String) 如果用户具有给定权限,则允许访问
hasIpAddress(String) 如果请求来自给定的IP地址,则允许访问
hasRole(String) 如果用户具有给定角色,则允许访问
not() 否定任何其他访问方法的影响
permitAll() 允许无条件访问
rememberMe() 允许通过remember-me进行身份验证的用户访问

大部分方法是为特定方式准备的,但是access(String)可以使用SpEL进一些特殊的设置,但其中很大一部分也和上面的方法相同:

表达式 作用
authentication 用户的身份验证对象
denyAll 始终评估为false
hasAnyRole(list of roles) 如果用户具有任何给定角色,则为true
hasRole(role) 如果用户具有给定角色,则为true
hasIpAddress(IP address) 如果请求来自给定的IP地址,则为true
isAnonymous() 如果用户是匿名用户,则为true
isAuthenticated() 如果用户已通过身份验证,则为true
isFullyAuthenticated() 如果用户已完全通过身份验证,则为true(未通过remember-me进行身份验证)
isRememberMe() 如果用户通过remember-me进行身份验证,则为true
permitAll 始终评估为true
principal 用户的主要对象

示例:

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.authorizeRequests()
  4. .antMatchers("/design", "/orders").access("hasRole('ROLE_USER')")
  5. .antMatchers(“/”, "/**").access("permitAll");
  6. }
  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.authorizeRequests()
  4. .antMatchers("/design", "/orders").access("hasRole('ROLE_USER') && " +
  5. "T(java.util.Calendar).getInstance().get("+"T(java.util.Calendar).DAY_OF_WEEK) == " + "T(java.util.Calendar).TUESDAY")
  6. .antMatchers(“/”, "/**").access("permitAll");
  7. }

2.配置登录页面

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.authorizeRequests()
  4. .antMatchers("/design", "/orders").access("hasRole('ROLE_USER')")
  5. .antMatchers(“/”, "/**").access("permitAll")
  6. .and()
  7. .formLogin()
  8. .loginPage("/login");
  9. }
  10. // 增加视图处理器
  11. @Overridepublic void addViewControllers(ViewControllerRegistry registry) {
  12. registry.addViewController("/").setViewName("home");
  13. registry.addViewController("/login");
  14. }

默认情况下,希望传递的是usernamepassword,当然你可以修改:

  1. .and()
  2. .formLogin()
  3. .loginPage("/login")
  4. .loginProcessingUrl("/authenticate")
  5. .usernameParameter("user")
  6. .passwordParameter("pwd")

也可修改默认登录成功的页面:

  1. .and()
  2. .formLogin()
  3. .loginPage("/login")
  4. .defaultSuccessUrl("/design")

3.配置登出

  1. .and()
  2. .logout()
  3. .logoutSuccessUrl("/")

4.csrf攻击

springsecurity默认开启了防止csrf攻击,你只需要在传递的时候加上:

  1. <input type="hidden" name="_csrf" th:value="${_csrf.token}"/>

当然,你也可以关闭,但是不建议这样做:

  1. .and()
  2. .csrf()
  3. .disable()

知道用户是谁

仅仅控制用户登录有时候是不够的,你可能还想在程序的其它地方获取已经登录的用户信息,有几种方式可以做到:

  • Principal对象注入控制器方法

  • Authentication对象注入控制器方法

  • 使用SecurityContextHolder获取安全上下文

  • 使用@AuthenticationPrincipal注解方法

1.将Principal对象注入控制器方法

  1. @PostMappingpublic String processOrder(@Valid Order order, Errors errors,SessionStatus sessionStatus,Principal principal) {
  2. ...
  3. User user = userRepository.findByUsername(principal.getName());
  4. order.setUser(user);
  5. ...
  6. }

2.将Authentication对象注入控制器方法

  1. @PostMappingpublic String processOrder(@Valid Order order, Errors errors, SessionStatus sessionStatus, Authentication authentication) {
  2. ...
  3. User user = (User) authentication.getPrincipal();
  4. order.setUser(user);
  5. ...
  6. }

3.使用SecurityContextHolder获取安全上下文

  1. Authentication authentication =
  2. SecurityContextHolder.getContext().getAuthentication();
  3. User user = (User) authentication.getPrincipal();

4.使用@AuthenticationPrincipal注解方法

  1. @PostMappingpublic String processOrder(@Valid Order order, Errors errors,SessionStatus sessionStatus, @AuthenticationPrincipal User user) {
  2. if (errors.hasErrors()) {
  3. return "orderForm";
  4. }
  5. order.setUser(user);
  6. orderRepo.save(order);
  7. sessionStatus.setComplete();
  8. return "redirect:/";
  9. }

使用SpringSecurity保护程序安全的更多相关文章

  1. 使用C#开发屏幕保护程序步骤

    本文介绍使用C#制作屏幕保护的方法,这个屏幕保护就是仿效视窗系统自带的字幕屏保. 屏幕保护程序的扩展名虽然是"scr",但其实是一个可执行的"exe"文件.但他 ...

  2. windows系统操作类和演示程序(关机,关闭显示器,打开屏幕保护程序,打开光驱等)

    /// <summary> /// 系统控制类,关机,关闭显示器,打开屏幕保存程序等 /// </summary> public class SystemPowerContro ...

  3. windows屏幕保护程序opengl模板

    Visual Studio 2013 屏幕保护程序opengl模板 ScreenSaver.cpp #define VC_EXTRALEAN #include <windows.h> #i ...

  4. SystemParametersInfo调置壁纸、屏幕保护程序

    应用SystemParametersInfo函数可以获取和设置数量众多的windows系统参数.这个小程序就是运用了SystemParametersInfo函数来设置桌面的墙纸,而且程序可以让我们选择 ...

  5. 使用 WPF 开发一个 Windows 屏幕保护程序

    最近有小伙伴问我如何可以让 Windows 静置一段时间不操作之后,显示一个特殊的界面.我想了想,屏幕保护程序可以做到这一点,而且,屏幕保护程序的开发也是非常简单的. 本文将介绍如何为 Windows ...

  6. Window权限维持(五):屏幕保护程序

    屏幕保护是Windows功能的一部分,使用户可以在一段时间不活动后放置屏幕消息或图形动画.众所周知,Windows的此功能被威胁参与者滥用为持久性方法.这是因为屏幕保护程序是具有.scr文件扩展名的可 ...

  7. 保护程序猿滴眼睛---修改VS 2012 编辑器颜色

    转载于http://blog.csdn.net/qing666888/article/details/8973216 字体,发现好多人选用 Consolas  ...确实挺好看的. 然后 修改背景色: ...

  8. c#制作一个屏幕保护程序

    代码已上传github 实现思路:纯黑窗体去边框,加入标签. 使用Timmer让windows 10标签运动.限制标签的行为. 代码: int deltX = 10;       int deltY ...

  9. 使用SpringSecurity保护你的Eureka.

    因为注册中心基本上都是自己的应用在使用,应用不是特别多,可以写死,如果应用很多,那么就写入数据库把 pom <dependency> <groupId>org.springfr ...

随机推荐

  1. d3.js实现柱形图,饼图以及折现图

    饼图 var width = 500; var height = 500; //处理数据 var dataset = [ 30 , 10 , 43 , 55 , 13 ]; var pie = d3. ...

  2. python 读取文件1

    1.脚本 from sys import argv script,filename = argv txt = open(filename) print ("the filename is % ...

  3. 记一次idea问题—performing vcs refresh...

    01.前言 本人出现该场景是,我把本地SVN A项目删了,而A项目与B项目同在一个SVN目录下,当我修改B项目且提交代码时,出现了该问题. idea不是很懂操作,就搜索了一下得出了三种答案,但只有其一 ...

  4. 基于TP5.1实用案例及教程

    推荐<基于TP5.1实用案例及教程>书 目录: 通用封装 Export通用封装Import通用封装配合Import通用封装的ImportBaseVerify类Files通用封装Direct ...

  5. Mobile game forensics

    My friend Carrie'd like to know "Garena 传说对决" violates any mobile risks such as insecure d ...

  6. Sring 的 @AliasFor 使用规则

    一.该标签存在的意义 顾名思义 @AliasFor 表示别名,它可以注解到自定义注解的两个属性上,表示这两个互为别名,也就是说这两个属性其实同一个含义.该标签存在的含义,从网上查发现有个点, 若  自 ...

  7. go单元测试

    testing模块 测试代码放在当前包以_test.go结尾的文件中 测试函数以Test为名称前缀 测试命令(go test) 正常编译操作(go build/install)会忽略测试文件 单例模式 ...

  8. spark shuffle写操作三部曲之BypassMergeSortShuffleWriter

    前言 再上一篇文章 spark shuffle的写操作之准备工作 中,主要介绍了 spark shuffle的准备工作,本篇文章主要介绍spark shuffle使用BypassMergeSortSh ...

  9. ssm 搭建项目各项配置

    首先配置 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&quo ...

  10. MyBatis 核心配置综述之 ResultSetHandler

    目录 ResultSetHandler 简介 ResultSetHandler 创建 ResultSetHandler 处理结果映射 DefaultResultSetHandler 源码解析 我们之前 ...