Spring Security 入门


1. 配置文件

WEB-INF/web.xml 

<!-- SpringSecurity过滤器链  -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 启动Spring  -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:applicationContext.xml
        classpath:spring-security.xml
    </param-value>
</context-param>
<!--启动SpringMVC-->
<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!-- 服务器启动加载Servlet-->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

springmvc.xml

<!-- 扫描Controller类-->
<contenxt:component-scan base-package="cn.bug"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!-- 后缀-->
    <property name="suffix" value=".jsp"/>
</bean>
<!--注解方式处理器映射器和处理器适配器 -->
<mvc:annotation-driven/>

2. 使用HttpBasic方式进行登录(认证)

pattern: 需要拦截资源
access: 拦截方式
        isFullyAuthenticated(): 该资源需要认证才可以访问
        isAnonymous():只有匿名用户才可以访问(如果登录用户就无法访问)
        permitAll():允许所有人(匿名和登录用户)方法

<security:http>
    <security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
    <!--使用HttpBasic方式进行登录(认证)-->
    <security:http-basic/>
</security:http>

<!--
   security:authentication-manager: 认证管理器
        1)认证信息提供方式(账户名,密码,当前用户权限)
-->
<security:authentication-manager>
    <security:authentication-provider>
        <security:user-service>
            <security:user name="root" password="123456" authorities="ROLE_USER"/>  
        </security:user-service>
    </security:authentication-provider>
</security:authentication-manager>

在游览器弹出的输入框里面,输入正确的用户名密码即可登陆 


3. form-login属性详解

form-login是spring security命名空间配置登录相关信息的标签,它包含如下属性:

  1. login-page 自定义登录页url,默认为/login
  2. login-processing-url 登录请求拦截的url,也就是form表单提交时指定的action
  3. default-target-url 默认登录成功后跳转的url
  4. always-use-default-target 是否总是使用默认的登录成功后跳转url
  5. authentication-failure-url 登录失败后跳转的url
  6. username-parameter 用户名的请求字段,默认为username
  7. password-parameter 密码的请求字段,默认为password
  8. authentication-success-handler-ref 指向一个AuthenticationSuccessHandler用于处理认证成功的请求,不能和default-target-url还有always-use-default-target同时使用
  9. authentication-failure-handler-ref 指向一个AuthenticationFailureHandler用于处理失败的认证请求
  10. authentication-success-forward-url 用于authentication-failure-handler-ref
  11. authentication-failure-forward-url 用于authentication-failure-handler-ref
  12. authentication-details-source-ref 指向一个AuthenticationDetailsSource,在认证过滤器中使用

处理拦截的几个Filter

UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication//打断点

String username = obtainUsername(request);
String password = obtainPassword(request);
ExceptionTranslationFilter
org.springframework.security.web.access.ExceptionTranslationFilter#doFilter//打断点

Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
FilterSecurityInterceptor
org.springframework.security.web.access.intercept.FilterSecurityInterceptor#invoke//打断点

InterceptorStatusToken token = super.beforeInvocation(fi);

3. 为每个用户指定不同的权限 

<security:http>
    <security:intercept-url pattern="/product/index" access="permitAll()"/>
    <security:intercept-url pattern="/userLogin" access="permitAll()"/>
    <security:intercept-url pattern="/product/add" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/product/update" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/product/list" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/product/delete" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>

    <security:form-login login-page="/userLogin" login-processing-url="/securityLogin"
                         default-target-url="/product/index"/>
    <!-- 自定义权限不足处理 -->
    <security:access-denied-handler error-page="/error"/>
    <!-- 关闭Spring Security CSRF机制 -->
    <security:csrf disabled="true"/>
</security:http>

<security:authentication-manager>
    <security:authentication-provider>
        <security:user-service>
            <security:user name="root" password="123456" authorities="ROLE_USER"/>
            <security:user name="jack" password="123456" authorities="ROLE_ADMIN"/>
        </security:user-service>
    </security:authentication-provider>
</security:authentication-manager>

4. 用户的用户名密码通过java类来实现 

<security:http>
    <security:intercept-url pattern="/product/index" access="permitAll()"/>
    <security:intercept-url pattern="/userLogin" access="permitAll()"/>
    <security:intercept-url pattern="/product/add" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/product/update" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/product/list" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/product/delete" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>

    <security:form-login login-page="/userLogin" login-processing-url="/securityLogin"
                         default-target-url="/product/index"/>
    <!-- 自定义权限不足处理 -->
    <security:access-denied-handler error-page="/error"/>
    <!-- 关闭Spring Security CSRF机制 -->
    <security:csrf disabled="true"/>

</security:http>

<security:authentication-manager>
    <!--  自定义UserDetailService方式-->
    <security:authentication-provider user-service-ref="myUserDetailService"/>
</security:authentication-manager>
<bean id="myUserDetailService" class="cn.bug.security.MyUserDetailService"/>

同步方式进行页面跳转(无法满足前后端分离开发)

public class MyUserDetailService implements UserDetailsService {
    /**
     * loadUserByUsername: 读取用户信息
     * @return 实现UserDetails即可
     *
     * ROLE_USER,ROLE_ADMIN
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //UserDetails: 封装用户数据的接口
        //操作数据库等等...
        return new User( "bug", "123456", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
    }
}

5. 返回自定义信息

<security:http>
    <security:intercept-url pattern="/product/index" access="permitAll()"/>
    <security:intercept-url pattern="/userLogin" access="permitAll()"/>
    <security:intercept-url pattern="/product/add" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/product/update" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/product/list" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/product/delete" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>

    <security:form-login login-page="/userLogin" login-processing-url="/securityLogin"
                         default-target-url="/product/index"
                         authentication-success-handler-ref="myAuthenticationSuccessHandler"
                         authentication-failure-handler-ref="myAuthenticationFailureHandler"/>
    <!-- 自定义权限不足处理 -->
    <security:access-denied-handler error-page="/error"/>
    <!-- 关闭Spring Security CSRF机制 -->
    <security:csrf disabled="true"/>
</security:http>

<security:authentication-manager>
    <security:authentication-provider user-service-ref="myUserDetailService"/>
</security:authentication-manager>

<bean id="myUserDetailService" class="cn.bug.security.MyUserDetailService"/>
<bean id="myAuthenticationSuccessHandler" class="cn.bug.security.MyAuthenticationSuccessHandler"/>
<bean id="myAuthenticationFailureHandler" class="cn.bug.security.MyAuthenticationFailureHandler"/>
/**
 * 验证失败后返回的自定义数据类型
 */
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    /**
     * ObjectMapper: jackson框架的工具类,用于转换对象为json字符串
     * <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
     * <dependency>
     *      <groupId>com.fasterxml.jackson.core</groupId>
     *      <artifactId>jackson-databind</artifactId>
     *      <version>2.9.5</version>
     * </dependency>
     */
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        //返回json字符串给前端
        Map<String, Object> result = new HashMap<>();
        result.put("succcess", false);
        String json = objectMapper.writeValueAsString(result);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}
/**
 * 登陆成功
 */
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private ObjectMapper objectMapper = new ObjectMapper();

    /**
     * @param authentication :代表认证成功后的信息
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //返回json字符串给前端
        Map<String,Object> result = new HashMap<>();
        result.put("succcess",true);
        String json = objectMapper.writeValueAsString(result);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}
public class MyUserDetailService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User( "eric","123456",AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
    }
}

6. 原理分析

//1.整体流程
                          用户
                            ||
                            \/                                
                UsernamePasswordAuthenticationFilter
                            ||
                            \/                    
                AuthenticationManager
                            ||
                            \/                    
                AuthenticationProvider ---认证失败---> AuthenticationFailureHandler
                            ||
                         认证成功
                            \/                    
                AuthenticationSuccessHandler

//2.源码分析
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication
                return this.getAuthenticationManager().authenticate(authRequest);
                                                ||
                                                \/
org.springframework.security.authentication.ProviderManager#authenticate    
                result = provider.authenticate(authentication);
                                                ||
                                                \/
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate
        org.springframework.security.authentication.AccountStatusUserDetailsChecker#check

 7. UserDetails

/**
 * 所有方法都需要返回true才能通过
 */
public class User implements UserDetails {
    /**
     * 返回当前用户所拥有的所有权限
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
    @Override
    public String getPassword() {
        return null;
    }
    @Override
    public String getUsername() {
        return null;
    }
    /**
     * 改账号是否过期
     * @return true不过期  false过期
     */
    @Override
    public boolean isAccountNonExpired() {
        return false;
    }
    /**
     * 账户是否锁定
     */
    @Override
    public boolean isAccountNonLocked() {
        return false;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }
    /**
     * 账号是否可用
     */
    @Override
    public boolean isEnabled() {
        return false;
    }
}
return new User( "eric","123456", true,true,false,true,AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));

<properties>
    <jdk.version>1.8</jdk.version>
    <spring.version>4.3.10.RELEASE</spring.version>
    <spring.security.version>4.2.3.RELEASE</spring.security.version>
    <jstl.version>1.2</jstl.version>
    <servlet.version>2.5</servlet.version>
</properties>

<dependencies>
    <!-- Spring dependencies -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>${spring.security.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>${spring.security.version}</version>
    </dependency>

    <!-- jstl for jsp page -->
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>${jstl.version}</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>${servlet.version}</version>
        <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.5</version>
    </dependency>

</dependencies>

https://blog.csdn.net/yin380697242/article/category/2434205

https://blog.csdn.net/yin380697242/article/details/51893397

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值