返回介绍

5.2 安全策略配置

发布于 2025-04-26 13:26:33 字数 7665 浏览 0 评论 0 收藏 0

关于系统的安全管理及各种设计,Spring Security 已经大体上都实现了,只需要进行一些配置和引用,就能够正常使用。如代码清单 5-2 所示,安全配置类 SecurityConfiguration 继承了 Spring Security 的 WebSecurityConfigurerAdapter。这里可以使用 HttpSecurity 的一些安全策略进行配置,各项配置的解释如下:

- loginPage:设置一个使用自定义的登录页面 URL。

- loginSuccessHandler:设置自定义的一个登录成功处理器。

- permitAll:是完全允许访问的一些 URL 配置,并可以使用通配符来设置,这里将一些资源目录赋予可以完全访问的权限,由 settings 指定的权限列表也赋予了完全访问的权限。

- logout:设置使用默认的登出。

- logoutSuccessUrl:设定登出成功的链接。

- rememberMe:用来记住用户的登录状态,即用户没有执行退出时,再次打开页面将不用登录。

- csrf:即跨站请求伪造(cross-site request forgery),这是一个防止跨站请求伪造攻击的策略设置。

- accessDeniedPage:配置一个拒绝访问的提示链接。

其中,settings 是引用了自定义的配置参数。

代码清单 5-2 安全策略配置

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private SecuritySettings settings;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().loginPage("/login").permitAll().successHandler(login
SuccessHandler())
                .and().authorizeRequests()
                .antMatchers("/images/**", "/checkcode", "/scripts/**", "/styles/**").permitAll()
                .antMatchers(settings.getPermitall().split(",")).permitAll()
                .anyRequest().authenticated()
                .and().csrf().requireCsrfProtectionMatcher(csrfSecurityRequestMatcher())
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .and().logout().logoutSuccessUrl(settings.getLogoutsuccssurl())
                .and().exceptionHandling().accessDeniedPage(settings.getDenied
page())
                .and().rememberMe().tokenValiditySeconds(1209600).tokenRepository(tokenRepository());
    }
    ......
}

5.2.1 权限管理规则

代码清单 5-2 中引用的 SecuritySettings 是自定义的一个配置类,如代码清单 5-3 所示。其中使用注解 @ConfigurationProperties 设定配置参数的前缀部分为 securityconfig,定义的各个配置参数的意义如下:

- logoutsuccessurl:用来定义退出成功的链接。

- permitall:用来定义允许访问的 URL 列表。

- deniedpage:用来设定拒绝访问的信息提示链接。

- urlroles:这是一个权限管理规则,是链接地址与角色权限的配置列表。

代码清单 5-3 自定义配置类

@ConfigurationProperties(prefix="securityconfig")
public class SecuritySettings {
    private String logoutsuccessurl = "/logout";
    private String permitall = "/api";
    private String deniedpage = "/deny";
    private String urlroles;
    ......
}

使用自定义配置参数后,可以在工程的配置文件 application.yml 中对安全管理进行集中配置,如代码清单 5-4 所示。

代码清单 5-4 使用自定义的 securityconfig 配置

securityconfig:
    logoutsuccssurl: /
    permitall: /rest/**,/bbs**
    deniedpage: /deny
    urlroles: /**/new = manage,admin;
            /**/edit/** = admin;
            /**/delete/** = admin

其中 urlroles 配置一个权限配置列表,这是我们设计的一种权限管理规则,列表中的每一个配置项用分号分隔,每一个配置项的等号左边是一个可以带上通配符的链接地址,等号右边是一个角色列表,角色之间用逗号分隔。每一个配置项表示包含等号左边字符串的链接地址,能够被等号右边的角色访问。

这将要求我们的控制器设计链接地址时,必须遵循这一权限管理规则,这样只要使用一个简单的配置列表,就能够覆盖整个系统的权限管理策略。设计控制器链接地址的规则如下,它包含了系统增删查改的所有操作。

- /**/new:新建;

- /**/edit/**:修改;

- /**/delete/**:删除;

- /**/show/**:查看;

- /**/list:列表查询。

使用这种规则之后,再来看看代码清单 5-4 中 urlroles 的权限配置,这里只需要简单的三个配置项,就已经完成了对一个应用系统所有权限的管理配置了。其中,新建操作只有 manage、admin 两个角色有权限,修改操作和删除操作只有 admin 这个角色有权限,至于没有在权限管理列表中配置的查看操作,因为没有限定角色访问,所以它能被所有用户访问。

这种权限策略配置好了之后,要让应用系统中的一个用户具有哪些权限,只要分配给这个用户一些角色就可以。

5.2.2 登录成功处理器

登录成功后,如果需要对用户的行为进行记录或者执行其他操作,可以使用登录成功处理器。代码清单 5-5 是一个登录成功处理器的定义,这里只是简单地输出了用户登录的日志。

代码清单 5-5 登录成功处理器

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException,ServletException {
        User userDetails = (User)authentication.getPrincipal();
        log.info("登录用户




user:" + userDetails.getName() + "login"+request.getContextPath());
        log.info("IP:" + getIpAddress(request));
        super.onAuthenticationSuccess(request, response, authentication);
}

5.2.3 防攻击策略

因为 Spring Security 的跨站请求伪造(cross-site request forgery,CSRF)即阻止跨站请求伪造攻击的功能很完善,所以使用 Spring Security 之后,对于新建、修改和删除等操作,必须进行特殊的处理,才能正常使用。这要求在所有具有上面操作请求的页面上提供如下代码片段,因为我们的页面设计使用了 Thymeleaf 模板,所以只要在 layout.hmtl 的页头上加入下面两行代码即可,loyou.html 是所有页面都会用到的一个页面文件。

<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>

还要在 loyout.html 中引用脚本文件 public.js,然后在 public.js 中增加一个函数,如代码清单 5-6 所示。这样做的意思是,在表单提交时放入一个 token,服务端验证该 token 是否有效,只允许有效的 token 请求,否则拒绝当前操作。这样就能够很好地起到防御 CSRF 攻击的目的。

代码清单 5-6 阻止 CSRF 攻击策略

$(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});

如果要对第三方开放接口,上面的方法就不适用了,这时只能对特定的 URL 使用排除 CSRF 保护的方法来实现。代码清单 5-7 对指定的 URL 排除对其进行 CSRF 的保护。

代码清单 5-7 排除 CSRF 保护策略

public class CsrfSecurityRequestMatcher implements RequestMatcher {
    protected Log log = LogFactory.getLog(getClass());
    private Pattern allowedMethods = Pattern
            .compile("^(GET|HEAD|TRACE|OPTIONS)$");
    /**
     * 需要排除的




url 列表





     */
    private List<String> execludeUrls;
    @Override
    public boolean matches(HttpServletRequest request) {
        if (execludeUrls != null && execludeUrls.size() > 0) {
            String servletPath = request.getServletPath();
            for (String url : execludeUrls) {
                if (servletPath.contains(url)) {
                    log.info("++++"+servletPath);
                    return false;
                }
            }
        }
        return !allowedMethods.matcher(request.getMethod()).matches();
    }
        ……





}

然后在配置类中,加入需要排除阻止 CSRF 攻击的链接列表,如代码清单 5-8 所示,只要链接地址中包含“/rest”字符串,就将对其忽略 CSRF 保护策略。

代码清单 5-8 在安全配置类加入需要排除 CSRF 保护的列表

private CsrfSecurityRequestMatcher csrfSecurityRequestMatcher(){
        CsrfSecurityRequestMatcher csrfSecurityRequestMatcher = new CsrfSecurity
RequestMatcher();
        List<String> list = new ArrayList<String>();
        list.add("/rest/");
        csrfSecurityRequestMatcher.setExecludeUrls(list);
        return csrfSecurityRequestMatcher;
   }

5.2.4 记住登录状态

代码清单 5-2 中的安全策略配置中有一行配置:rememberMe().tokenValiditySeconds(86400).tokenRepository(tokenRepository()),它是用来记住用户登录状态的一个配置,其中 86400 指定记住的时间秒数,即为 1 天时间。为了实现这个功能,需要将一个用户的登录令牌等信息保存在数据库中,这需要在配置类中指定连接数据库的数据源,如代码清单 5-9 所示。

代码清单 5-9 指定保存登录用户 token 的数据源

@Autowired @Qualifier("dataSource")
private DataSource dataSource;
@Bean
    public JdbcTokenRepositoryImpl tokenRepository(){
        JdbcTokenRepositoryImpl jtr = new JdbcTokenRepositoryImpl();
        jtr.setDataSource(dataSource);
        return jtr;
    }

同时,还应该在数据库中增加一个数据表 persistent_logins,这个表结构的定义是由 Spring Security 提供的,使用一个实体来实现,这样做的目的只是为了在系统启动时能够创建这个表结构而已,如代码清单 5-10 所示,它用来保存用户名、令牌和最后登录时间等信息。

代码清单 5-10 记住用户登录状态的实体建模

@Entity
@Table(name = "persistent_logins")
public class PersistentLogins implements java.io.Serializable{
    @Id
    @Column(name = "series", length = 64, nullable = false)
    private String series;
    @Column(name = "username", length = 64, nullable = false)
    private String username;
    @Column(name = "token", length = 64, nullable = false)
    private String token;
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "last_used", nullable = false)
private Date last_used;……





}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。