前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Security学习(二)

Spring Security学习(二)

作者头像
allsmallpig
发布2021-02-25 10:25:18
8370
发布2021-02-25 10:25:18
举报
文章被收录于专栏:allsmallpi博客

以下配置基于表单登录配置

自定义配置登录页面

代码语言:javascript
复制
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
             // 自定义页面路径
            .loginPage("/api/login")
            .and()
            .authorizeRequests()
            // 允许/api/login的URL访问 否则浏览器页面将提示重定向次数过多进入死循环
            .antMatchers("/api/login").permitAll()
            .anyRequest()
            .authenticated();
}

自定义登录路径

代码语言:javascript
复制
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
             .loginPage("/api/login")
            // 自定义登录路径
            .loginProcessingUrl("/api/formlogin") 
            .and()
            .authorizeRequests()
            .antMatchers("/api/login").permitAll()
            .anyRequest()
            .authenticated();
}

Spring Security 默认表单登录

代码语言:javascript
复制
public UsernamePasswordAuthenticationFilter() {
    super(new AntPathRequestMatcher("/login", "POST"));
}

可设置loginProcessingUrl属性来替换默认登录地址

登录成功处理

当用户登录成功后需要保存用户登录数据,比如IP地址,登录时间等

代码语言:javascript
复制
// 注入SecurityLoginSuccessHandler
@Autowired
private SecurityLoginSuccessHandler securityLoginSuccessHandler; 

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
            .loginPage("/api/login")
            // 自定义登录成功处理
            .successHandler(securityLoginSuccessHandler)
            .loginProcessingUrl("/api/formlogin")
            .and()
            .authorizeRequests()
            .antMatchers("/api/login","/static/*").permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .csrf().disable();
}

SecurityLoginSuccessHandler

代码语言:javascript
复制
@Component
public class SecurityLoginSuccessHandler implements AuthenticationSuccessHandler {
    // 重定向跳转类
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    // 缓存请求路径,可获取拦截前的请求路径
    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("登录成功");
        // 获取拦截前请求路径 并通过RedirectStrategy进行重定向跳转
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        String redirectUrl = savedRequest.getRedirectUrl();
        
         // todo do something
        
        this.redirectStrategy.sendRedirect(request, response, redirectUrl);
    }
}

登录失败处理

跟登录成功处理一样 只是实现接口不同而已

代码语言:javascript
复制
@Autowired
private SecurityLoginFailHandler securityLoginFailHandler;
    
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
            .loginPage("/api/login")
            .successHandler(securityLoginSuccessHandler)
            // 自定义登录失败处理
            .failureHandler(securityLoginFailHandler)
            .loginProcessingUrl("/api/formlogin")
            .and()
            .authorizeRequests()
            .antMatchers("/api/login", "/static/*").permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .csrf().disable();
}

SecurityLoginFailHandler

代码语言:javascript
复制
@Component
public class SecurityLoginFailHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        System.out.println("登录失败");
        
        // todo do something
        
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/json;charset=UTF-8");
        response.getWriter().write(exception.getMessage());
    }
}

退出登录

默认是访问URL/logout将注销登陆的用户

自定义配置

代码语言:javascript
复制
@Autowired
private SecurityLoginoutHandler securityLoginoutHandler;
@Autowired
private SecurityLoginoutSuccessHandler securityLoginoutSuccessHandler;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
            .loginPage("/api/login")
            .successHandler(securityLoginSuccessHandler)
            .failureHandler(securityLoginFailHandler)
            .loginProcessingUrl("/api/formlogin")
            .and()
            .authorizeRequests()
            .antMatchers("/api/login", "/static/*").permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .logout()
            // 退出登录地址
            .logoutUrl("/logout")
            // 退出跳转路径
            .logoutSuccessUrl("/api/login")
            // 默认session失效
            .invalidateHttpSession(true)
            // 退出登录处理
            .addLogoutHandler(securityLoginoutHandler)
            // 退出登录成功处理
            .logoutSuccessHandler(securityLoginoutSuccessHandler)
            // 指定退出登录后需要删除的cookie名称,多个cookie之间以逗号分隔。 
//                .deleteCookies(cookieNamesToClear)
            .and()
            .csrf().disable();
}

SecurityLoginoutHandler

用来执行必要的清理,因而他们不应该抛出错误

代码语言:javascript
复制
@Component
public class SecurityLoginoutHandler implements LogoutHandler {
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        System.out.println("退出登录");
    }
}

SecurityLoginoutSuccessHandler

被LogoutFilter在成功注销后调用,用来进行重定向或者转发相应的目的地。注意这个接口与LogoutHandler几乎一样,但是可以抛出异常。

代码语言:javascript
复制
@Component
public class SecurityLoginoutSuccessHandler implements LogoutSuccessHandler {
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("退出登录成功执行");
        this.redirectStrategy.sendRedirect(request, response, "/");
    }
}

自定义用户登录账号密码

WebSecurityConfig

代码语言:javascript
复制
@Autowired
private SecurityUserDetailService securityUserDetailService;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    // 在内存中写死用户数据
    auth.inMemoryAuthentication().withUser("123456").password("123456").roles("USER");
    // 动态获取用户数据
    auth.userDetailsService(securityUserDetailService);
}

SecurityUserDetailService

代码语言:javascript
复制
/**
 * 自定义UserDetailService
 *
 * @author Peng
 */
@Component
public class SecurityUserDetailService implements UserDetailsService {
    /**
     * String password; 密码
     * String username; 账户名
     * Set authorities; 角色名
     * boolean accountNonExpired; 账户没有过期
     * boolean accountNonLocked; 帐号没有被锁定
     * boolean credentialsNonExpired; 密码没有过期
     * boolean enabled; 是否可用
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // 这里写死的密码 实际应用中应该从数据库中获取 
        User user = new User(username, "123456", true,
                true,
                true,
                true,
                AuthorityUtils.commaSeparatedStringToAuthorityList("USER"));
        return user;
    }
}

自定义用户登录账号密码(密码加密)

WebSecurityConfig

代码语言:javascript
复制
@Autowired
private SecurityUserDetailService securityUserDetailService;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    // 在内存中写死用户数据
    auth.inMemoryAuthentication().withUser("123456").password("123456").roles("USER");
    // 动态获取用户数据 使用Security加密
    auth.userDetailsService(securityUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
     
    // 配置验证方式为加密验证 
    DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
    authenticationProvider.setUserDetailsService(securityUserDetailService);
    authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
    auth.authenticationProvider(authenticationProvider);
}

SecurityUserDetailService

代码语言:javascript
复制
/**
 * 自定义UserDetailService
 *
 * @author Peng
 */
@Component
public class SecurityUserDetailService implements UserDetailsService {
    /**
     * String password; 密码
     * String username; 账户名
     * Set authorities; 角色名
     * boolean accountNonExpired; 账户没有过期
     * boolean accountNonLocked; 帐号没有被锁定
     * boolean credentialsNonExpired; 密码没有过期
     * boolean enabled; 是否可用
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 这里写死的密码 实际应用中应该从数据库中获取 
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        // 数据库创建用户数据时密码应通过Security加密后保存
        String password = encoder.encode("123456");
        System.out.println(password);
        User user = new User(username, password, true,
                true,
                true,
                true,
                AuthorityUtils.commaSeparatedStringToAuthorityList("USER"));
        return user;
    }
}

PasswordEncoder

代码语言:javascript
复制
// 密码加密
String encode(CharSequence rawPassword);
// 验证密码是否吻合
boolean matches(CharSequence rawPassword, String encodedPassword);

获取登录用户信息

代码语言:javascript
复制
/**
 * 获取登录用户数据
 */
@RequestMapping("/api/getme")
@ResponseBody
public Object getLoginUser(Authentication authentication) {
    Map<String, Object> json = new HashMap<>();
    // 两种方法
    json.put("data", SecurityContextHolder.getContext().getAuthentication());
    json.put("data1", authentication);
    return json;
}

资源权限限制

WebSecurityConfig

代码语言:javascript
复制
 .antMatchers("/api/me").hasRole("ADMIN")
 
 这样只要是访问“api/me”的路径就会验证用户身份,用户身份必须是ADMIN才允许访问

表达

描述

hasRole([role])

如果当前主体具有指定的角色,则返回true.

hasAnyRole([role1,role2])

如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true

hasAuthority([authority])

如果当前的主体具有指定的权限,则返回 true.

hasAnyAuthority([authority1,authority2])

如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true.

principal

允许直接访问表示当前用户的主对象

permitAll

允许所有用户访问

denyAll

不允许用户访问

isAnonymous()

如果当前的主体是一个匿名用户,则返回true.

isRememberMe()

如果当前的主体是一个匿名用户,则返回true

isAuthenticated()

如果用户不是匿名的,则返回 true

isFullyAuthenticated()

如果用户不是一个匿名的或是一个记住我的用户返回true

hasPermission(Object target, Object permission)

如果用户已访问给定权限的提供的目标,则返回true,例如hasPermission(domainObject, 'read')

hasPermission(Object targetId, String targetType, Object permission)

如果用户已访问给定权限的提供的目标,则返回true,例如hasPermission(1, 'com.example.domain.Message', 'read')

登录验证码

首先得写一个获取验证码方法,方法能够生成随机验证码并且把验证码存入session供验证的功能,这里就不写了

因为Spring Security未提供验证码的接口,所以需要我们自己写一个过滤器处理

VaildCodeFilter 验证码校验过滤器

代码语言:javascript
复制
/**
 * Security 登录验证码验证
 *
 * @author Peng
 */
public class VaildCodeFilter extends OncePerRequestFilter {
    private SecurityLoginFailHandler securityLoginFailHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 登录路径必须为/api/formlogin才生效
        if ("/api/formlogin".equals(request.getRequestURI())) {
            HttpSession session = request.getSession();
            try {
                String code = request.getParameter(页面验证码字段);
                String sessionCode = (String) session.getAttribute(验证码sessionId);
                if (code == null || sessionCode == null) {
                    throw new VaildCodeException("验证码不存在");
                }
                if (!sessionCode.equals(code)) {
                    throw new VaildCodeException("验证码不匹配");
                }
            } catch (VaildCodeException e) {
                securityLoginFailHandler.onAuthenticationFailure(request, response, e);
                return;
            }
            session.removeAttribute(验证码sessionId);
        }
        filterChain.doFilter(request, response);
    }

    public void setSecurityLoginFailHandler(SecurityLoginFailHandler securityLoginFailHandler) {
        this.securityLoginFailHandler = securityLoginFailHandler;
    }
}

最后在WebSecurityConfig configure中配置

代码语言:javascript
复制
  @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 在UsernamePasswordAuthenticationFilter过滤器前添加自定义验证码过滤器
        VaildCodeFilter vaildCodeFilter = new VaildCodeFilter();
        vaildCodeFilter.setSecurityLoginFailHandler(securityLoginFailHandler);
        http.addFilterBefore(vaildCodeFilter, UsernamePasswordAuthenticationFilter.class);
        
        http.formLogin().loginPage("/api/login")
                .successHandler(securityLoginSuccessHandler)
                .failureHandler(securityLoginFailHandler)
                .loginProcessingUrl("/api/formlogin");
        http.authorizeRequests()
                .antMatchers("/api/login", "/static/*", "/api/codeImg").permitAll()
                .antMatchers("/api/me").hasRole("ADMIN")
                .anyRequest()
                .authenticated();
        http.logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/api/login")
                .invalidateHttpSession(true)
                .addLogoutHandler(securityLoginoutHandler)
                .logoutSuccessHandler(securityLoginoutSuccessHandler);
        // 指定退出登录后需要删除的cookie名称,多个cookie之间以逗号分隔。
//                .deleteCookies(cookieNamesToClear)
        http.csrf().disable();
    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/01/16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义配置登录页面
  • 自定义登录路径
  • 登录成功处理
  • 登录失败处理
  • 退出登录
  • 自定义用户登录账号密码
  • 自定义用户登录账号密码(密码加密)
  • 获取登录用户信息
  • 资源权限限制
  • 登录验证码
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档