重构用户名密码登录获取token_centos通过用户名和密码获取token-程序员宅基地

技术标签: Spring Security  

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

用户名密码登录获取token

WhaleAuthenticationSuccessHandler

AuthenticationSuccessHandler
在成功处理器中根据请求头解析出client-id
在这里插入图片描述
参考 org.springframework.security.web.authentication.www.BasicAuthenticationFilter#doFilterInternal

@Component("whaleAuthenticationSuccessHandler")
public class WhaleAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

	private Logger logger = LoggerFactory.getLogger(getClass());

	// ObjectMapper spring mvc 提供的
	@Autowired
	private ObjectMapper objectMapper;

	@Autowired
	private SecurityProperties securityProperties;

	@Autowired
	private ClientDetailsService clientDetailsService;

	@Autowired
	private  AuthorizationServerTokenServices authorizationServerTokenServices;


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.web.authentication.
	 * AuthenticationSuccessHandler#onAuthenticationSuccess(javax.servlet.http.
	 * HttpServletRequest, javax.servlet.http.HttpServletResponse,
	 * org.springframework.security.core.Authentication)
	 */
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
										Authentication authentication) throws IOException, ServletException {

		logger.info("登录成功");

		String header = request.getHeader("Authorization");

		if (header == null || !header.toLowerCase().startsWith("basic ")) {
//			chain.doFilter(request, response);
			throw new UnapprovedClientAuthenticationException("请求头中无client信息");
//			return;
		}
//		try {
		String[] tokens = extractAndDecodeHeader(header, request);
		assert tokens.length == 2;

		String username = tokens[0];

		String clientId = tokens[0];
		String clientSecret = tokens[1];

		//拿到了clientDetails
		ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
        //校验
		if(clientDetails==null){
			throw new UnapprovedClientAuthenticationException("client-id对应信息不存在:"+clientId);
		}else if(! StringUtils.equals(clientDetails.getClientSecret(),clientSecret)){
			throw new UnapprovedClientAuthenticationException("client-secret对应信息不匹配:"+clientSecret);
		}

		//不需要再构建authentication 传一个空的map
		//grantType 为四种授权模式  我们是自定义的
		TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP,clientId,clientDetails.getScope(),"custom");

		OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);

		OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);

		OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);



		response.setContentType("application/json;charset=UTF-8");
//		response.getWriter().write(objectMapper.writeValueAsString(authentication));
		response.getWriter().write(objectMapper.writeValueAsString(oAuth2AccessToken));

//		if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
//			response.setContentType("application/json;charset=UTF-8");
//			response.getWriter().write(objectMapper.writeValueAsString(authentication));
//		}else {
//			//如果配置的返回类型不是json,则调用父类方法,进行跳转
//			super.onAuthenticationSuccess(request,response,authentication);
//		}

	}

		/**
		 * Decodes the header into a username and password.
		 *
		 * @throws BadCredentialsException if the Basic header is not present or is not valid
		 * Base64
		 */
		private String[] extractAndDecodeHeader(String header, HttpServletRequest request)
			throws IOException {

			byte[] base64Token = header.substring(6).getBytes("UTF-8");
			byte[] decoded;
			try {
				decoded = Base64.getDecoder().decode(base64Token);
			}
			catch (IllegalArgumentException e) {
				throw new BadCredentialsException(
						"Failed to decode basic authentication token");
			}

//			String token = new String(decoded, getCredentialsCharset(request));
			String token = new String(decoded, "UTF-8");

			int delim = token.indexOf(":");

			if (delim == -1) {
				throw new BadCredentialsException("Invalid basic authentication token");
			}
			return new String[] { token.substring(0, delim), token.substring(delim + 1) };
		}
}

WhaleResourceServerConfig

参考 BrowserSecurityConfig

@Configuration
@EnableResourceServer
public class WhaleResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    protected AuthenticationSuccessHandler whaleAuthenticationSuccessHandler;

    @Autowired
    protected AuthenticationFailureHandler whaleAuthenticationFailureHandler;

    @Autowired
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;

    @Autowired
    private SecurityProperties securityProperties;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        super.configure(http);

        http.formLogin()
                .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
                .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM)
                .successHandler(whaleAuthenticationSuccessHandler)
                .failureHandler(whaleAuthenticationFailureHandler);

        http//.apply(validateCodeSecurityConfig)
               //.and()
                .apply(smsCodeAuthenticationSecurityConfig)
                .and()
               /*
               APP上没有rember的概念
               .rememberMe()
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSecodes())*/
               /* .userDetailsService(userDetailsService)
                .and()
                .sessionManagement()

                .invalidSessionStrategy(invalidSessionStrategy)
                .maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions())
                .maxSessionsPreventsLogin(securityProperties.getBrowser().getSession().isMaxSessionsPreventsLogin())
                .expiredSessionStrategy(sessionInformationExpiredStrategy)*/

//                .invalidSessionUrl("/session/invalid")
//                .maximumSessions(1)//为1 后面登录的session会把前面的登录的session失效掉
//                .maxSessionsPreventsLogin(true)//当session数量达到最大时 阻止后面的登录
//                .expiredSessionStrategy(new MyExpiredSessionStrategy())//并发登录导致超时的处理策略
//                .and()
//                .and()
//                .logout()
//                .logoutUrl("/signOut")
//                .logoutSuccessUrl("/logOut.html")
//                .logoutSuccessHandler(LogoutSuccessHandler) //Handler和Url是互斥的
//                .deleteCookies("JESSIONID")//清除cookie中的 当前session id
//                .and()
                .authorizeRequests()
                .antMatchers(
                        SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
                        SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
                        securityProperties.getBrowser().getLoginPage(),
                        SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*",
                        securityProperties.getBrowser().getSession().getSessionInvalidUrl()+".json",
                        securityProperties.getBrowser().getSession().getSessionInvalidUrl()+".html",
                        "/session/invalid")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .cors().disable().csrf().disable();// 禁用跨站攻击

    }
}

启动测试获取token

模拟表单登录获取token
在这里插入图片描述在这里插入图片描述

拿着token请求资源
在这里插入图片描述
直接浏览器访问会报错
在这里插入图片描述

重构短信登录获取token

测试用手机验证获取token

在这里插入图片描述

WhaleResourceServerConfig

在这里插入图片描述这个不能配置,因为如果加上了验证码校验的配置,表单获取token的时候也就需要验证码了,
继而不能请求发送手机短信的token,后面再说吧
解决
ValidateCodeFilter 注释掉表单登录的验证码验证

//        urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM, ValidateCodeType.IMAGE);

ok

拿着token请求发送手机短信验证码的接口

上次的token9995dfa3-f3c5-441d-9e3a-032ab62eec02
在这里插入图片描述
只需要在请求头上加上token就可以请求资源

在这里插入图片描述

postman用手机短信码去获取token及问题


我们的postman是模拟浏览器请求,会带上cookie,cookie里面又还有session id 可以找到session
但是如果我们用代码去发送请求,它是没有cookie也没有session 所有 会报一个短信验证码不存在的错误
因为我们的验证码是存在session里面的

所有app中我们不能用session去存储手机验证码

重构验证码存储逻辑

在这里插入图片描述

@Autowired

@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。

验证码存储 ValidateCodeRepository

在这里插入图片描述

ValidateCodeRepository

public interface ValidateCodeRepository  {


    /**
     * 保存验证码
     * @param request
     * @param validateCode
     * @param validateCodeType
     */
    void save(ServletWebRequest request ,ValidateCode validateCode,ValidateCodeType validateCodeType);


    /**
     * 获取验证码
     * @param request
     * @param validateCodeType
     * @return
     */
    ValidateCode get(ServletWebRequest request,ValidateCodeType validateCodeType);


    /**
     * 移除验证码
     * @param request
     * @param validateCodeType
     * @return
     */
    void remove(ServletWebRequest request,ValidateCodeType validateCodeType);
}

RedisValidateCodeRepository

app端我们把验证码存到redis里面
在这里插入图片描述

@Component
public class RedisValidateCodeRepository implements ValidateCodeRepository {

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    /**
     * 保存验证码
     *
     * @param request
     * @param validateCode
     * @param validateCodeType
     */
    @Override
    public void save(ServletWebRequest request, ValidateCode validateCode, ValidateCodeType validateCodeType) {
        //存redis 30分钟超时时间
        redisTemplate.opsForValue().set(buildKey(request,validateCodeType),validateCode,30,TimeUnit.MINUTES);
    }

    /**
     * 获取验证码
     * @param request
     * @param validateCodeType
     * @return
     */
    @Override
    public ValidateCode get(ServletWebRequest request, ValidateCodeType validateCodeType) {
        Object value = redisTemplate.opsForValue().get(buildKey(request, validateCodeType));
        if(value==null){
            return null;
        }
        return (ValidateCode) value;
    }

    /**
     * 移除验证码
     *
     * @param request
     * @param validateCodeType
     * @return
     */
    @Override
    public void remove(ServletWebRequest request, ValidateCodeType validateCodeType) {
        redisTemplate.delete(buildKey(request,validateCodeType));
    }

    private String buildKey(ServletWebRequest request, ValidateCodeType validateCodeType) {
        String deviceId = request.getHeader("deviceId");
        if(StringUtils.isBlank(deviceId)){
            throw new ValidateCodeException("请在请求头中携带deviceId参数");
        }
        return "code:"+validateCodeType.toString().toLowerCase()+":"+deviceId;

    }

    /**
     * 根据请求的url获取校验码的类型
     *
     * @param request
     * @return
     */
    private ValidateCodeType getValidateCodeType(ServletWebRequest request) {
        String type = StringUtils.substringBefore(getClass().getSimpleName(), "CodeProcessor");
        return ValidateCodeType.valueOf(type.toUpperCase());
    }
}
SessionValidateCodeRepository

浏览器端还是存到session中国

在这里插入图片描述

`@Component
public class SessionValidateCodeRepository implements ValidateCodeRepository {


    /**
     * 操作session的工具类
     */
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    /**
     * 保存验证码
     *
     * @param request
     * @param validateCode
     * @param validateCodeType
     */
    @Override
    public void save(ServletWebRequest request, ValidateCode validateCode, ValidateCodeType validateCodeType) {
        ValidateCode code = new ValidateCode(validateCode.getCode(),validateCode.getExpireTime());
        sessionStrategy.setAttribute(request, getSessionKey(request), code);
    }

    /**
     * 获取验证码
     *
     * @param request
     * @param validateCodeType
     * @return
     */
    @Override
    public ValidateCode get(ServletWebRequest request, ValidateCodeType validateCodeType) {

        String sessionKey = getSessionKey(request);

        ValidateCode codeInSession = (ValidateCode) sessionStrategy.getAttribute(request, sessionKey);

        return codeInSession;
    }

    /**
     * 移除验证码
     *
     * @param request
     * @param validateCodeType
     * @return
     */
    @Override
    public void remove(ServletWebRequest request, ValidateCodeType validateCodeType) {
        sessionStrategy.removeAttribute(request,getSessionKey(request));
    }


    /**
     * 构建验证码放入session时的key
     *
     * @param request
     * @return
     */
    private String getSessionKey(ServletWebRequest request) {
//		return SESSION_KEY_PREFIX + getProcessorType(request);
        return SESSION_KEY_PREFIX + getValidateCodeType(request).toString().toUpperCase();

    }

    /**
     * 根据请求的url获取校验码的类型
     *
     * @param request
     * @return
     */
    private ValidateCodeType getValidateCodeType(ServletWebRequest request) {
        String type = StringUtils.substringBefore(getClass().getSimpleName(), "CodeProcessor");
        return ValidateCodeType.valueOf(type.toUpperCase());
     }

  }

`

AbstractValidateCodeProcessor 更换验证码存储接口

引入

	@Autowired
	private ValidateCodeRepository validateCodeRepository;

来对应操作验证码

在依赖app的时候就会找到我们的RedisValidateCodeRepository 的bean

redis配置

## Redis服务器地址
spring.redis.host=192.168.85.134
## Redis服务器连接端口
spring.redis.port=6379
## Redis服务器连接密码(默认为空)
spring.redis.password=123456

测试

在这里插入图片描述在这里插入图片描述测试成功

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/uotail/article/details/90441309

智能推荐

DataInputStream与DataOutputStream的使用-程序员宅基地

所以我们就不需要再关注具体读取的长度,只需关注读取的基本数据类型。当然,使用DataInputStream读取的文件内容需要是用DataOutputStream写入的。我们在使用时需要知道文件中储存的数据类型和储存的顺序,必须根据当时写入文件的顺序来一一对照着读取,不能跳过。在读取String类型时需要用readUTF()...

myeclipse中卸载jrebel-程序员宅基地

大家好,这几天想给myeclipse装一个热部署,确遇到一个棘手的问题,安装错版本如何卸载? 1.打开myeclipse-help-install from site .2.选择红框中选项 3.搜索到需要卸载的插件 4.全部选择卸载 结束,已经测试,完全可用...

C#可视化技术编程:DBHelper类扩展版-程序员宅基地

代码如下:using System;using System.Collections.Generic;using System.Data;using System.Data.SqlClient;using System.Linq;using System.Text;using System.Threading.Tasks;namespace product6._10{ class DBHelper { //创建公共dataSet 对象 pub_dbhelper

BootStrap 模态框简单原型_bootstrap 原型界面-程序员宅基地

BootStrap 模态框简单原型 &lt;button class="btn btn-sm btn-default" data-toggle="modal" data-target="#tees" &gt; 添加&lt;/button&gt;&lt;div class="modal fade" id="tees" role="dia_bootstrap 原型界面

强大的矩阵奇异值分解(SVD)及其应用-程序员宅基地

版权声明: 本文由LeftNotEasy发布于http://leftnoteasy.cnblogs.com, 本文可以被全部的转载或者部分使用,但请注明出处,如果有问题,请联系[email protected]。也可以加我的微博: @leftnoteasy前言: 上一次写了关于PCA与LDA的文章,PCA的实现一般有两种,一种是用特征值分解去实现的,一种是用

linux使得python后台运行,linux 下后台运行python脚本-程序员宅基地

&符号这两天要在服务器端一直运行一个Python脚本,当然就想到了在命令后面加&符号$ python /data/python/server.py >python.log &说明:1、 > 表示把标准输出(STDOUT)重定向到 那个文件,这里重定向到了python.log2、 & 表示在后台执行脚本这样可以到达目的,但是,我们退出shell窗口的时候,..._linux 执行python后挂后台

随便推点

X86_64架构下的LINUX缓冲区溢出栈分析-程序员宅基地

缓冲区溢出基础:#include #include int main(int argc, char *argv[]) { char str[10]; strcpy(str, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); return 0;}这里strcpy的时候,字符长度超出了str所允许的长度,发生了溢出。当我们运行这个代码的时候,程序

jQuery select下拉框赋值_jq给select输入框赋值-程序员宅基地

本文介绍给下拉框和输入框赋值,代码如下:<!DOCTYPE html><html><head><meta charset="utf-8"><title>select</title></head><body> <label class="w3-label">请求领域<..._jq给select输入框赋值

YouTube视频规格-程序员宅基地

当一个视频上传到YouTube之后,YouTube会将视频和音频数据保存为不同版本的质量级别。视频分辨率有如下几种:240p、360p、480p、720p、1080p以及Original(原始视频分辨率,是指比1080p大的任意视频尺寸),分辨率越高视频就会越清晰。当增加视频分辨率的时候,YouTube也会对应播放更高质量的音频。下表列出了在Yo...

裸机S3C6410显示控制器(3)-LINUX下LCD驱动源码阅读_sec_hdmi_pll-程序员宅基地

相关文件:linux-2.6.28.6\drivers\video\samsung\s3cfb.c (这个是LCD驱动的入口)linux-2.6.28.6\drivers\video\samsung\s3cfb_spi.clinux-2.6.28.6\drivers\video\samsung\s3cfb_fimd4x.c (操作6410LCD硬件的代码)linux-2.6.28._sec_hdmi_pll

sizeof(struct)的结果分析及其原因_sizeof(struct1_s)-程序员宅基地

sizeof是面试经常考的知识点,现我从网上转载了一篇文章讲的很精彩,可以参考!转载地址:http://blog.csdn.net/xinjixjz/article/details/6769344有的时候,在脑海中停顿了很久的“显而易见”的东西,其实根本上就是错误的。就拿下面的问题来看: struct T { char ch; int i ; }; 使用size..._sizeof(struct1_s)

@Configuration 和 @Bean_@configuration not bean-程序员宅基地

本文转载自博客园:https://www.cnblogs.com/soundcode/p/6478004.html1. @Bean:1.1 定义从定义可以看出,@Bean只能用于注解方法和注解的定义。@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTI..._@configuration not bean

推荐文章

热门文章

相关标签