SpringSecurity使用

Spring Security

Spring Security简介

SpringSecurity是基于Spring应用提供的声明式的安全保护性的框架,它可以在web请求级别的和方法调用级别处理身份和授权。他是基于AspectJ的切面经行配置的

Spring Security的模块

Spring
Security有11个模块:ASL、切面(Aspect)、CAS、Config、Core、加密(Cryptographiy)、LDAP、OpenID、Remoting、TagLib、Web,对应Maven上的几个模块

过滤WEB请求

Spring
Security使用很多Filter来提供安全性能。我们使用DelegatingFilterProxy——一个特殊的Servlet
FIlter。它会将工作为委托给Filter的实现类。只需要把这个Bean注入再Spring的上下文里就行了

使用xml配置的话,在web.xml里配置filter就行了。我们展示在使用注解的配置。在config包下建立一个SercurtyWebInitializer类

/**
 * @author surface
 * 安全操作的初始化类,里面啥都不用写
 */
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {

}

如果只是初始化DelegatingFilterProxy的话,我们什么都不用干。这样注册之后,它都会拦截应用中的请求,并且把他委托给Id为springSecurityFilterChain的bean。springSecurityFilterChain也是一个特殊的filter,他可以连接其他filter。但是,我们并不需要知道这些Filter的细节,因为我们并不需要显式的声明springSecurityFilterChain以及和它连接的filter。

最简单的安全性配置

使用类来配置最简单的安全性配置,然后添加到RootConfig里去

(EnableWebMcvSecurity已经被弃用)

/**
 * @author surface
 * 安全性设置
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

}

当我们需要指定Web的安全细节,则需要重写WebSecurityConfigurationAdapter的一些方法,比如configure方法

  • configure(WebSecurity) 配置Security的filter链

  • configure(HttpSecurity) 配置如何通过连接器的保护

  • configure(AuthenticationManagerBuilder) 配置user-detail服务

为了让我们的配置符合我们的要求,我们还需要配置一些东西

  • 用户储存

  • 指定哪些请求需要验证,那些请求不需要验证,以及需要的权限

  • 提供一个自定义的登录页

处理以上的内容外,我们可能还需要有选择性的在view上显示内容

选择查询用户详细信息的服务

内存用户储存:重写configure(AuthenticationManagerBuilder)方法

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()       //使用内存用户储存
                //配置了一个user用户和一个admin用户
                .withUser("user").password("password").roles("USER").and()
                .withUser("admin").password("password").roles("USER", "ADMIN");
    }

基于数据库的认证

这里由于配置了MyBatis的DataSource,所以可以直接住居进来

我们的Spring Security内部已经配置好了一些内定了SQL语句,默认的表结构如下

user表:username password enable

authority表:username authority

grop表:id grip_name

我们也可以自定义查询,用来鉴权。只需要使用Security提供的底层bean类来实现。把我们自己的实体类转换成Secu底层的user

创建一个UserDetailsServiceImpl的Service,给出@Service标签,在继承UserDetailsService并且重写loadUserByUserName方法。在重写的这个方法里面把自己的User实体转换成Security的User实体

@Service("UserDetailService")
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    SysUserMapper sysUserMapper;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        SysUser sysUser = null;
        if (userName.matches(PHONE_REGEX)) {
            sysUser = sysUserMapper.selectByPhone(userName);
        } else {
            sysUser = sysUserMapper.selectByUsername(userName);
        }
        User userDetails = new User(sysUser.getUserName(), sysUser.getPassword(), handleAuthority(sysUser.getAuthority()));
        return userDetails;
    }
}

其中,GrantedAuthority是为了做权限控制而搞的,用一个字符串即可区分。后面就可以用它来做权限控制

然后把这个UserDetailsServiceImpl丢给SecurityConfig做判断

14c58cca3241435d21fc1532b4594469.png
@Override
    /**
     * 配置整个用户信息从哪里获得
     */
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }

还有一个问题就是明文的密码储存。为了安全,我们需要给用户输入的密码进行转码

@Override
    /**
     * 配置整个用户信息从哪里获得
     */
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService)
                //密码加密
                .passwordEncoder(passwordEncoder());
    }

LDAP认证(略过 )

配置自定义用户的服务(略过)

拦截请求

对每个请求进行详细的安全性控制在于重载configure(HttpSecurity)方法,它为不同的url路径都有选择地应用安全性

 @Override
    /**
     * 安全配置的详细信息
     */
    protected void configure(final HttpSecurity http) throws Exception {
        http.authorizeRequests()    //下面是详细的安全性调整
            .antMatch("/splitters/me").authenticated()
            .anyResult().permiAll();
    }

首先调用的authorizeRequest方法,然后使用它返回的对象来配置各种细节。

第一次调用的antMatchers()指定了对“/spitters/me”路径的请求进行验证;第二次则更为具体,指定了对“/spittles”的post请求进行验证,后面的anyRequest则说明了对其他的访问则是允许的。

我们也可以使用通配符进行比对

.antMatch("/splitters/**").authenticated()

antMatcher使用的是ant风格的路径匹配模式。

Apache
Ant样式的路径有三种通配符匹配方法(在下面的表格中列出)这些可以组合出很多种灵活的路径模式:

58bcec455310b604b86ae4f615090b38.png

下面列举了所有的可以进行验证的方式

40b14c2018e84c0e8305669abcc0febf.jpg
af5484b7836e0d3136f72d0155561072.jpg

还需要注意的是,在排列这些方法的时候,需要把最具体的规则放在前面,最不具体的规则放在后面。这样不具体的规则就不会覆盖到前面的

当一个权限不够的用户试图访问的时候,会抛出403错误

  1. 使用Spring表达式进行安全保护

  2. 强制通道的安全性

  3. 防止跨站请求

为了防止跨站请求,一些敏感的url会需要一个token来进行防伪。它会拦截一些变化状态的请求,并且检查里面是否有这个“_csrf”域的参数并且检查是否正确

当然我们可以禁用csrf功能,但是这样非常不推荐

protected void configure(final HttpSecurity http) throws Exception {
    http
        ...
        .and().csrf().disable();

认证用户

在一开始使用什么都没有的配置的时候,SpringSecurity就会帮你配置默认的登陆界面。在配置之后,你也可以使用如下的配置来启用这个配置。它会配置在你的“/longin”路径下。使用内存登入就会把用户名密码存在内存里,可以用于测试。

这里有一份比较详细的配置,包括配置自己的登陆登出界面和回调函数,全大写的变量是常量,请自行对比(我才不会说原来的示例代码源码掉了。。。)

protected void configure(final HttpSecurity http) throws Exception {
    http
        ...
        //设置登陆请求的URL
            .and().formLogin()
            .loginProcessingUrl(LOGIN_URL)
            .successForwardUrl(LOGIN_SUCCESS_URL)
            .failureForwardUrl(LOGIN_FAIL_URL)
            .usernameParameter(USER_NAME_PARAMETER)
            .passwordParameter(PASSWORD_PARAMETER)
            //设置登出
            .and().logout().logoutUrl(LOGOUT_URL)
            .logoutSuccessUrl(LOGOUT_SUCCESS_URL)
            .deleteCookies("JSESSIONID")
            //设置拒绝时候的url
            .and().exceptionHandling().accessDeniedPage("/error/403")
            //设置未登录的操作
            .and().exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
                @Override
                public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                    if (httpServletRequest.getCookies()!=null && httpServletRequest.getCookies().length > 0) {
                        logger.info("未登录的cookie:" + httpServletRequest.getCookies()[0].getName()+":"+httpServletRequest.getCookies()[0].getValue());
                    }
                    logger.info("未登录访问的URI:"+httpServletRequest.getRequestURI()+" 方法:"+httpServletRequest.getMethod());
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    String sb = "{\"code\":10002,\"message\":\"未登录\",\"data\":null}";
                    out.write(sb);
                    out.flush();
                    out.close();
                }
            })

在jsp中,需要注意,如果还开启csrf_token验证的话,需要在表单里面添加_csrf参数

启用Remember-me功能

    http
    ...
    .and().remeberMe().tokenValiditySeconds(3600).key("key")

这样就会储存一个cooike并且设置一小时后过期。这个cooike储存了用户名和密码,现在这个cooike的名字叫“key”

在登陆的时候需要用用一个复选框,启用记住我功能。似乎只需要在启用的时候设置一个name是remember-me的复选框并且勾选时,就可以启用

<input name="remeber-me" yupe="checkbox">记住我</input>

退出

按照我们的配置,现在已经可以退出了,我们只需要做一个使用该功能的连接,直接在配置文件里添加如下的代码

    http
    ...
    //设置登出
         .and().logout().logoutUrl(LOGOUT_URL)
         .logoutSuccessUrl(LOGOUT_SUCCESS_URL)
         .deleteCookies("JSESSIONID")

所以我只能只能手动实现这个等出功能,创建一个等处的控制器,里面代码如下


后来发现使用配置经行logout的配置时,在csrf功能开启的时候,只能post提交/logout并且要带有token。也就是说请求的时候不能手动输入/logout,需要使用连接,点击连接的时候传入csrf的token参数,才能登出成功.

保护视图

Jsp的SpringSecurity标签库,跳过


附加 Spring Boot集成Spring Security

导入Maven依赖

  • 在Maven中导入符合Spring Boot的启动依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • 这配置好启动后,访问就会让你登陆,


    082fac24e95b894dac574e7fb0746cb9.png
  • 用户名是user,密码会在log中显示
278b393023dee8675e4a57cd20bf1f4e.png

然后剩下的就可以创建Security Config来配置了**

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容