Spring Security原理与应用

Spring Security是Spring提供登录认证,鉴权的组件,使用起来很方便,所以越来越多的系统使用Spring Security完成登录校验功能,Spring Security使用Filter过滤器检查客户端连接是否完成登录校验,使用Cookie记录登录状态,下面例程演示Spring Securiy登录和授权。

1、创建Maven工程,引入Spring Securiy依赖


  <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.0.0.RELEASE</version>
      <relativePath/>
  </parent>
  
  <dependencies>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

2、创建配置文件,/src/main/resources/application.yml

server:
  port: 6060
  

3、编写启动类

package com.hk.sec;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StartApp {
	public static void main(String[] args)
	{
		SpringApplication.run(StartApp.class, args);
	}
}

4、测试Spring Security
对Spring Security没有做更多配置,默认登录用户是user,密码在启动控制台上有显示
在这里插入图片描述
通过浏览器登录系统http://localhost:6060
在这里插入图片描述
登录成功后出现
在这里插入图片描述
5、以上是最简化地使用了Spring Security,在实际项目中需要对登录和授权做更细致的控制,比如用户和密码放在DB,LDAP或者第三方平台登录(qq,微信),授权控制需要通过角色来控制权限。
6、集成数据库用户和密码校验,实现接口UserDetailsService,接口UserDetailsService是Spring Security中定义的接口,Spring Security使用UserDetailsService获取用户对象

package com.hk.sec;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

@Component
public class UserService implements UserDetailsService{

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		System.out.println("loadUserByUsername username==="+username);
		if("user".equals(username))
		{
			UserInfo u = new UserInfo("user","123");
			u.addRole("ROLE_ORG");
			u.addRole("ROLE_USER");
			return u;
		}
		
		return null;
	}

}

在类UserService 可以连接数据库将用户名和密码加载出来。
7、配置如何使用自定义类UserService获取用户数据
定义WebSecurityConfigurerAdapter子类,并实现configure方式,通过 定义类UserService获取用户信息

@Configuration
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	UserDetailsService uds;
	
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	auth.userDetailsService(uds);
    	
    	
		//auth.inMemoryAuthentication()
		//.withUser("orguser").password("123").roles("ORG","USER")
		//.and()
		//.withUser("user").password("123").roles("USER")
		//.and()
		//.withUser("roleuser").password("123").roles("ROLE");
    }
}

这样就可以使用数据库中的用户和密码进行系统校验。

8、如何使用角色控制资源的访问
编写三个Controller,分别对应三个访问权限

@RestController
public class OrgCtrl {
	@GetMapping("/org")
    public String hello() {
        return "Org";
    }
}

@RestController
public class RoleCtrl {
	@GetMapping("/role")
    public String hello() {
        return "Role";
    }
}

@RestController
public class UserCtrl {
	@GetMapping("/user")
    public String hello() {
        return "User";
    }
}

9、配置WebSecurityConfigurerAdapter子类中的configure方法

package com.hk.sec;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	UserDetailsService uds;
	
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	auth.userDetailsService(uds);
    	
    	
		//auth.inMemoryAuthentication()
		//.withUser("orguser").password("123").roles("ORG","USER")
		//.and()
		//.withUser("user").password("123").roles("USER")
		//.and()
		//.withUser("roleuser").password("123").roles("ROLE");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .antMatchers("/org/**")
        .hasRole("ORG")// /org资源必须具有ORG角色
        
        .antMatchers("/user/**")
        .hasRole("USER")// /user资源必须具有USER角色
               
        .antMatchers("/orguser/**")
        .access("hasRole('ORG') and hasRole('USER')") // /orguser资源必须具有USER角色并且具有ORG角色
               
        .antMatchers("/role/**")
        .access("hasAnyRole('ORG') and  hasRole('ROLE')")
        .anyRequest()
        .authenticated()
        .and()
        .csrf()
        .disable();
   }
}

10、自定义登录页面

package com.hk.sec;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	UserDetailsService uds;
	
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	auth.userDetailsService(uds);
    	
    	
		//auth.inMemoryAuthentication()
		//.withUser("orguser").password("123").roles("ORG","USER")
		//.and()
		//.withUser("user").password("123").roles("USER")
		//.and()
		//.withUser("roleuser").password("123").roles("ROLE");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .antMatchers("/org/**")
        .hasRole("ORG")
        
        .antMatchers("/user/**")
        .hasRole("USER")
               
        .antMatchers("/orguser/**")
        .access("hasRole('ORG') and hasRole('USER')")
               
        .antMatchers("/role/**")
        .access("hasAnyRole('ORG') and  hasRole('ROLE')")
        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        .loginPage("/loginpage") //自定义登录页面
        .loginProcessingUrl("/logindone")
        .permitAll()
        .and()
        .csrf()
        .disable();
     }
}

11、对前后端分离的登录认证支持,前后端分离登录返回JSON对象,不能跳转页面,所以需要定义返回信息

package com.hk.sec;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	UserDetailsService uds;
	
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	auth.userDetailsService(uds);
    	
    	
		//auth.inMemoryAuthentication()
		//.withUser("orguser").password("123").roles("ORG","USER")
		//.and()
		//.withUser("user").password("123").roles("USER")
		//.and()
		//.withUser("roleuser").password("123").roles("ROLE");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .antMatchers("/org/**")
        .hasRole("ORG")
        
        .antMatchers("/user/**")
        .hasRole("USER")
               
        .antMatchers("/orguser/**")
        .access("hasRole('ORG') and hasRole('USER')")
               
        .antMatchers("/role/**")
        .access("hasAnyRole('ORG') and  hasRole('ROLE')")

              
        .anyRequest()
        .authenticated()
        .and()
              
        .formLogin()
        .loginProcessingUrl("/logindone")
        .successHandler(new AuthenticationSuccessHandler() {            //登陆成功后
            @Override
            public void onAuthenticationSuccess(HttpServletRequest req,
                                                HttpServletResponse resp,
                                                Authentication auth)    //当前用户登陆信息
                    throws IOException {
                Object principal = auth.getPrincipal();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                resp.setStatus(200);
                Map<String, Object> map = new HashMap<>();
                map.put("status", 200);
                map.put("msg", principal);
                ObjectMapper om = new ObjectMapper();
                out.write(om.writeValueAsString(map));
                out.flush();
                out.close();
            }
        })
        .failureHandler(new AuthenticationFailureHandler() {         //登陆失败后
            @Override
            public void onAuthenticationFailure(HttpServletRequest req,
                                                HttpServletResponse resp,
                                                AuthenticationException e)  //获取登陆失败原因
                    throws IOException {
            	e.printStackTrace();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                resp.setStatus(401);
                Map<String, Object> map = new HashMap<>();
                map.put("status", 401);
                if (e instanceof LockedException) {
                    map.put("msg", "账户被锁定,登录失败!");
                } else if (e instanceof BadCredentialsException) {
                    map.put("msg", "账户名或密码输入错误,登录失败!");
                } else if (e instanceof DisabledException) {
                    map.put("msg", "账户被禁用,登录失败!");
                } else if (e instanceof AccountExpiredException) {
                    map.put("msg", "账户已过期,登录失败!");
                } else if (e instanceof CredentialsExpiredException) {
                    map.put("msg", "密码已过期,登录失败!");
                } else {
                    map.put("msg", "登录失败!");
                }
                ObjectMapper om = new ObjectMapper();
                out.write(om.writeValueAsString(map));
                out.flush();
                out.close();
            }
        })
        .permitAll()
        .and()
        .logout()//开启注销登陆
        .logoutUrl("/logout")//注销登陆请求url
        .clearAuthentication(true)//清除身份信息
        .invalidateHttpSession(true)//session失效
        .addLogoutHandler(new LogoutHandler() {//注销处理
            @Override
            public void logout(HttpServletRequest req,
                               HttpServletResponse resp,
                               Authentication auth) {

            }
        })
        .logoutSuccessHandler(new LogoutSuccessHandler() {     //注销成功处理
            @Override
            public void onLogoutSuccess(HttpServletRequest req,
                                        HttpServletResponse resp,
                                        Authentication auth)
                    throws IOException {
                resp.sendRedirect("/login_page");              //跳转到自定义登陆页面
            }
        })
        .and()
        .csrf()
        .disable();

    }

}

12、对应用户密码错误、用户失效、用户被锁等场景,Spring Secruity都提供解决方案

        .failureHandler(new AuthenticationFailureHandler() {         //登陆失败后
            @Override
            public void onAuthenticationFailure(HttpServletRequest req,
                                                HttpServletResponse resp,
                                                AuthenticationException e)  //获取登陆失败原因
                    throws IOException {
            	e.printStackTrace();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                resp.setStatus(401);
                Map<String, Object> map = new HashMap<>();
                map.put("status", 401);
                if (e instanceof LockedException) {
                    map.put("msg", "账户被锁定,登录失败!");
                } else if (e instanceof BadCredentialsException) {
                    map.put("msg", "账户名或密码输入错误,登录失败!");
                } else if (e instanceof DisabledException) {
                    map.put("msg", "账户被禁用,登录失败!");
                } else if (e instanceof AccountExpiredException) {
                    map.put("msg", "账户已过期,登录失败!");
                } else if (e instanceof CredentialsExpiredException) {
                    map.put("msg", "密码已过期,登录失败!");
                } else {
                    map.put("msg", "登录失败!");
                }
                ObjectMapper om = new ObjectMapper();
                out.write(om.writeValueAsString(map));
                out.flush();
                out.close();
            }
        })

代码下载
链接:https://pan.baidu.com/s/1b4uzATldm5CeuzHrtXsnQA
提取码:3i8f

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值