温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

SpringSecurity中怎么保护程序安全

发布时间:2021-07-24 14:49:38 来源:亿速云 阅读:145 作者:Leah 栏目:编程语言

SpringSecurity中怎么保护程序安全,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

首先,引入依赖:

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

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

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

配置SpringSecurity

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

@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {  }

配置用户认证方式

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

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

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

@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {  @Override  protected void configure(AuthenticationManagerBuilder auth) throws Exception {  }}

1.基于内存

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {  auth.inMemoryAuthentication()      .withUser("zhangsan").password("123").authorities("ROLE_USER")      .and()      .withUser("lisi").password("456").authorities("ROLE_USER");}

2.基于JDBC

@AutowiredDataSource dataSource;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {  auth.jdbcAuthentication()    .dataSource(dataSource);}

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

public static final String DEF_USERS_BY_USERNAME_QUERY =  "select username,password,enabled " +  "from users " +  "where username = ?";public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =  "select username,authority " +  "from authorities " +  "where username = ?";public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =   "select g.id, g.group_name, ga.authority " +  "from groups g, group_members gm, group_authorities ga " +  "where gm.username = ? " +  "and g.id = ga.group_id " +  "and g.id = gm.group_id";

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

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {   auth.jdbcAuthentication().dataSource(dataSource)    .usersByUsernameQuery("select username, password, enabled from Users " +               "where username=?")    .authoritiesByUsernameQuery("select username, authority from UserAuthorities " +                  "where username=?");

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

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {   auth.jdbcAuthentication().dataSource(dataSource)    .usersByUsernameQuery("select username, password, enabled from Users " +               "where username=?")    .authoritiesByUsernameQuery("select username, authority from UserAuthorities " +                  "where username=?")    .passwordEncoder(new StandardPasswordEncoder("53cr3t");

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

BCryptPasswordEncoder  NoOpPasswordEncoder  Pbkdf2PasswordEncoder  SCryptPasswordEncoder  StandardPasswordEncoder(SHA-256)

3.基于LDAP

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {  auth.ldapAuthentication()    .userSearchBase("ou=people")    .userSearchFilter("(uid={0})")    .groupSearchBase("ou=groups")    .groupSearchFilter("member={0}")    .passwordCompare()    .passwordEncoder(new BCryptPasswordEncoder())    .passwordAttribute("passcode")    .contextSource()      .root("dc=tacocloud,dc=com")      .ldif("classpath:users.ldif");

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

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

@Datapublic class User implements UserDetails {  private Long id;  private String username;  private String password;  private String fullname;  private String city;  private String phoneNumber;    @Override  public Collection<? extends GrantedAuthority> getAuthorities() {    return null;  }  @Override  public boolean isAccountNonExpired() {    return false;  }  @Override  public boolean isAccountNonLocked() {    return false;  }  @Override  public boolean isCredentialsNonExpired() {    return false;  }  @Override  public boolean isEnabled() {    return false;  }}

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

@Servicepublic class UserService implements UserDetailsService {    @Override  public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {    return null;  }  }

最后,进行应用:

@Autowiredprivate UserDetailsService userDetailsService;@Beanpublic PasswordEncoder encoder() {  return new StandardPasswordEncoder("53cr3t");}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {  auth.userDetailsService(userDetailsService)    .passwordEncoder(encoder());}

配置认证路径

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

@Overrideprotected void configure(HttpSecurity http) throws Exception {   }

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

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

1.保护请求

@Overrideprotected void configure(HttpSecurity http) throws Exception {   http.authorizeRequests()    .antMatchers("/design", "/orders").hasRole("ROLE_USER")    .antMatchers(“/”, "/**").permitAll();}

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

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      用户的主要对象

示例:

@Overrideprotected void configure(HttpSecurity http) throws Exception {   http.authorizeRequests()    .antMatchers("/design", "/orders").access("hasRole('ROLE_USER')")    .antMatchers(“/”, "/**").access("permitAll");}

方法作用
表达式作用

@Overrideprotected void configure(HttpSecurity http) throws Exception {  http.authorizeRequests()    .antMatchers("/design", "/orders").access("hasRole('ROLE_USER') && " +     "T(java.util.Calendar).getInstance().get("+"T(java.util.Calendar).DAY_OF_WEEK) == " +     "T(java.util.Calendar).TUESDAY")    .antMatchers(“/”, "/**").access("permitAll");}

2.配置登录页面

@Overrideprotected void configure(HttpSecurity http) throws Exception {   http.authorizeRequests()    .antMatchers("/design", "/orders").access("hasRole('ROLE_USER')")    .antMatchers(“/”, "/**").access("permitAll")    .and()    .formLogin()      .loginPage("/login");}// 增加视图处理器@Overridepublic void addViewControllers(ViewControllerRegistry registry) {  registry.addViewController("/").setViewName("home");   registry.addViewController("/login");}

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

.and()  .formLogin()    .loginPage("/login")    .loginProcessingUrl("/authenticate")    .usernameParameter("user")    .passwordParameter("pwd")

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

.and()  .formLogin()    .loginPage("/login")    .defaultSuccessUrl("/design")

3.配置登出

.and()  .logout()     .logoutSuccessUrl("/")

4.csrf攻击

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

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

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

.and()  .csrf()    .disable()

知道用户是谁

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

将Principal对象注入控制器方法  将Authentication对象注入控制器方法  使用SecurityContextHolder获取安全上下文  使用@AuthenticationPrincipal注解方法

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

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

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

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

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

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

4.使用@AuthenticationPrincipal注解方法

@PostMappingpublic String processOrder(@Valid Order order, Errors errors,SessionStatus sessionStatus, @AuthenticationPrincipal User user) {  if (errors.hasErrors()) {    return "orderForm";   }   order.setUser(user);  orderRepo.save(order);  sessionStatus.setComplete();  return "redirect:/";}

关于SpringSecurity中怎么保护程序安全问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI