首先,引入依赖: org.springframework.boot spring-boot-starter-security 引入此依赖之后,你的web程序将拥有以下功能: 所有请求路径都需要认证 不需要特定的角色和权限 没有登录页面,使用HTTP基本身份认证 只有一个用户,名称为user 配置SpringSecurity springsecurity配置项,最好保存在一个单独的配置类中: @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { } 配置用户认证方式 首先,要解决的就是用户注册,保存用户的信息。springsecurity提供四种存储用户的方式: 基于内存(生产肯定不使用) 基于JDBC 基于LDAP 用户自定义(最常用) 使用其中任意一种方式,需要覆盖configure(AuthenticationManagerBuilder auth)方法: @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { } } 1.基于内存 @Override protected 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 @Autowired DataSource dataSource; @Override protected 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"; 当然,你可以对这些语句进行一下修改: @Override protected 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也提供了相应的功能: @Override protected 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 @Override protected 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接口,实现这个接口的目的是为框架提供更多的信息,你可以把它看作框架使用的实体类: @Data public 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 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,剩下的对比判断交个框架完成: @Service public class UserService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { return null; } } 最后,进行应用: @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder encoder() { return new StandardPasswordEncoder("53cr3t"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(encoder()); } 配置认证路径 知道了如何认证,但现在有几个问题,比如,用户登录页面就不需要认证,可以用configure(HttpSecurity http)对认证路径进行配置: @Override protected void configure(HttpSecurity http) throws Exception { } 你可以通过这个方法,实现以下功能: 在提供接口服务前,判断请求必须满足某些条件 配置登录页面 允许用户注销登录 跨站点伪造请求防护 1.保护请求 @Override protected 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 用户的主要对象 示例: @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/design", "/orders").access("hasRole('ROLE_USER')") .antMatchers(“/”, "/**").access("permitAll"); } @Override protected 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.配置登录页面 @Override protected 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攻击,你只需要在传递的时候加上: 当然,你也可以关闭,但是不建议这样做: .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:/"; } 标签: springboot, springsecurity, spring 好文要顶 关注我 收藏该文 _元夕 关注 - 0 粉丝 - 0 +加关注 0 0 « 上一篇: Linux安装Nginx posted @ 2019-09-04 09:55 _元夕 阅读(123) 评论(1) 编辑 收藏 https://www.cnblogs.com/NameZZH/p/11457146.html