springSecurity前后端分离及源码分析

springSecurity的功能在这里不再赘述,这里只介绍 前后端分离时如何配置以及springSecurity认证流程的分析。
一 在开发中以token的方式来进行认证用户的合法性。token的生成显的尤为重要。介绍一种生成token的生成方式JWT.。大家可以自行百度如何生成token

引入依赖
<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
 </dependency>
public static String  encryptToken(User user) {
		  String token = Jwts.builder()
	                .setSubject(user.getUsername())
	                .claim("authorities",user.getAuthorities())
	                .setExpiration(new Date(System.currentTimeMillis() + 7 * 60 * 1000))
	                .signWith(SignatureAlgorithm.HS512, "sallt")
	                .compact();
	        System.out.println(token);
		return token;
	}
	
	public static Map<String,Object> decryptToken(String token) {
		Map<String,Object> reslut=new HashMap<String ,Object>();
		Claims claims = Jwts.parser()
				.setSigningKey("sallt")
				.parseClaimsJws(token)
				.getBody();
		String userName=claims.getSubject();
		@SuppressWarnings("unchecked")
		Collection<GrantedAuthority> authorities= (Collection<GrantedAuthority>) claims.get("authorities");
		Date date=claims.getExpiration();
		reslut.put("userName", userName);
		reslut.put("authorities", authorities);
		reslut.put("expiredate", date);

		return reslut;
	}

二 继承WebSecurityConfigurerAdapter 配置认证权限

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetaiImpl userDetailService;
    
    @Autowired
    private JWTAuthenticationFilter jwtAuthenticationfilter;
	
    
	@Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new      UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		log.info("配置安全信息开始------------------------>");
		http//表示所有的授权都需要经过认证
		    .formLogin()//使用表单登入
		    .and().authorizeRequests().antMatchers("/user/login")
		    .anonymous()
		    .antMatchers("/user/getUser").hasAnyAuthority("admin")
		    .anyRequest().authenticated()
		    .and().cors().disable()
		    .sessionManagement()
		    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)//使用jwt验证拒绝使用sessIon
		    .and().csrf().disable()
		    .addFilterBefore(jwtAuthenticationfilter, UsernamePasswordAuthenticationFilter.class)
		    ;
		
	}
	
	@Bean
	public AuthenticationManager getAuthenticationManager() throws Exception {
	return 	super.authenticationManager();
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder() );
	}

三 继承UserDetailService 。在这里代码不再进行展示

四 写token拦截器。也是最最最最最重要的一步!!!。

String header=request.getHeader("Authorization");
		Enumeration<String> names=request.getHeaderNames();
		if(StringUtils.isNotBlank(header)) { //如果不为空
			header=StringUtils.substringAfter(header, "Bearer ");
			Map<String,Object> map=JwtUtils.decryptToken(header);
			User user=new User((String)map.get("userName"),"",AuthorityUtils.createAuthorityList("admin"));
			UsernamePasswordAuthenticationToken authentication=new 
					UsernamePasswordAuthenticationToken(user,null,AuthorityUtils.createAuthorityList("admin"));
			SecurityContextHolder.getContext().setAuthentication(authentication);
		}
			filterChain.doFilter(request, response);

SecurityContextHolder.getContext().setAuthentication(authentication); 这一步是尤为重要。这个设置是为后面的过滤器提供使用。即用最简单明了的一句话来说,在一次请求中需要设置用户的上下文。

五 源码分析
1)常用的过滤器
UsernamePasswordAuthenticationFilter
BasicAuthenticationFilter

FilterSecurityInterceptor (最后一步过滤器)

springSecurity会根据登入方式的不同来指定使用UsernamePasswordAuthenticationFilter还是BasicAuthenticationFilter过滤器。
2)假设用户是用户密码登入进来。源码分析如下:

UsernamePasswordAuthenticationFilter->ProviderManager->AbstractUserDetailsAuthenticationProvider>DaoAuthenticationProvider->>UserDetaiLService->FilterSecurityInterceptor

3)UsernamePasswordAuthenticationFilter源码分析

public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		}

		String username = obtainUsername(request);
		String password = obtainPassword(request);

		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		username = username.trim()
		 //封装token UsernamePasswordAuthenticationToken extens AbstractAuthenticationToken implements          Authentication
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);
       //调用 ProviderManager.authenticate
		return this.getAuthenticationManager().authenticate(authRequest);
	}

4)ProviderManager源码分析

public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		Authentication result = null;
		boolean debug = logger.isDebugEnabled();

		for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) { //找出合适的实现类
				continue;
			}

			if (debug) {
				logger.debug("Authentication attempt using "
						+ provider.getClass().getName());
			}

			try {
				result =   provider.authenticate(authentication);//AbstractUserDetailsAuthenticationProvider.authenticate

				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException e) {
				prepareException(e, authentication);
				// SEC-546: Avoid polling additional providers if auth failure is due to
				// invalid account status
				throw e;
			}
			catch (InternalAuthenticationServiceException e) {
				prepareException(e, authentication);
				throw e;
			}
			catch (AuthenticationException e) {
				lastException = e;
			}
		}

		if (result == null && parent != null) {
			// Allow the parent to try.
			try {
				result = parent.authenticate(authentication);
			}
			catch (ProviderNotFoundException e) {
				// ignore as we will throw below if no other exception occurred prior to
				// calling parent and the parent
				// may throw ProviderNotFound even though a provider in the child already
				// handled the request
			}
			catch (AuthenticationException e) {
				lastException = e;
			}
		}

		if (result != null) {
			if (eraseCredentialsAfterAuthentication
					&& (result instanceof CredentialsContainer)) {
				// Authentication is complete. Remove credentials and other secret data
				// from authentication
				((CredentialsContainer) result).eraseCredentials();
			}

			eventPublisher.publishAuthenticationSuccess(result);
			return result;
		}

		// Parent was null, or didn't authenticate (or throw an exception).

		if (lastException == null) {
			lastException = new ProviderNotFoundException(messages.getMessage(
					"ProviderManager.providerNotFound",
					new Object[] { toTest.getName() },
					"No AuthenticationProvider found for {0}"));
		}

		prepareException(lastException, authentication);

		throw lastException;
	}

5)AbstractUserDetailsAuthenticationProvider源码分析

public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
				messages.getMessage(
						"AbstractUserDetailsAuthenticationProvider.onlySupports",
						"Only UsernamePasswordAuthenticationToken is supported"));

		// Determine username
		String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
				: authentication.getName();

		boolean cacheWasUsed = true;
		UserDetails user = this.userCache.getUserFromCache(username);

		if (user == null) {
			cacheWasUsed = false;

			try {
				user = retrieveUser(username,
						(UsernamePasswordAuthenticationToken) authentication);
			}
			catch (UsernameNotFoundException notFound) {
				logger.debug("User '" + username + "' not found");

				if (hideUserNotFoundExceptions) {
					throw new BadCredentialsException(messages.getMessage(
							"AbstractUserDetailsAuthenticationProvider.badCredentials",
							"Bad credentials"));
				}
				else {
					throw notFound;
				}
			}

			Assert.notNull(user,
					"retrieveUser returned null - a violation of the interface contract");
		}

		try {
			preAuthenticationChecks.check(user);
			additionalAuthenticationChecks(user,
					(UsernamePasswordAuthenticationToken) authentication);
		}
		catch (AuthenticationException exception) {
			if (cacheWasUsed) {
				// There was a problem, so try again after checking
				// we're using latest data (i.e. not from the cache)
				cacheWasUsed = false;
				user = retrieveUser(username,
						(UsernamePasswordAuthenticationToken) authentication);//进入到DaoAuthenticationProvider
			
				preAuthenticationChecks.check(user);//检查用户的合法性是不是过期 被锁定
				additionalAuthenticationChecks(user,//检查用户密码的是不是匹配
						(UsernamePasswordAuthenticationToken) authentication);
			}
			else {
				throw exception;
			}
		}

		postAuthenticationChecks.check(user);

		if (!cacheWasUsed) {
			this.userCache.putUserInCache(user);
		}

		Object principalToReturn = user;

		if (forcePrincipalAsString) {
			principalToReturn = user.getUsername();
		}

		return createSuccessAuthentication(principalToReturn, authentication, user);
	}

6)DaoAuthenticationProvider源码分析

try {     //调用我们自己写的userdetilService loadbyusername();
			UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
			if (loadedUser == null) {
				throw new InternalAuthenticationServiceException(
						"UserDetailsService returned null, which is an interface contract violation");
			}
			return loadedUser;
		}
		catch (UsernameNotFoundException ex) {
			mitigateAgainstTimingAttack(authentication);
			throw ex;
		}
		catch (InternalAuthenticationServiceException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
		}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值