技术标签: Spring Security
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) };
}
}
参考 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,后面再说吧
解决
ValidateCodeFilter 注释掉表单登录的验证码验证
// urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM, ValidateCodeType.IMAGE);
ok
上次的token9995dfa3-f3c5-441d-9e3a-032ab62eec02
只需要在请求头上加上token就可以请求资源
我们的postman是模拟浏览器请求,会带上cookie,cookie里面又还有session id 可以找到session
但是如果我们用代码去发送请求,它是没有cookie也没有session 所有 会报一个短信验证码不存在的错误
因为我们的验证码是存在session里面的
所有app中我们不能用session去存储手机验证码
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
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);
}
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());
}
}
浏览器端还是存到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());
}
}
`
引入
@Autowired
private ValidateCodeRepository validateCodeRepository;
来对应操作验证码
在依赖app的时候就会找到我们的RedisValidateCodeRepository 的bean
## Redis服务器地址
spring.redis.host=192.168.85.134
## Redis服务器连接端口
spring.redis.port=6379
## Redis服务器连接密码(默认为空)
spring.redis.password=123456
测试成功
所以我们就不需要再关注具体读取的长度,只需关注读取的基本数据类型。当然,使用DataInputStream读取的文件内容需要是用DataOutputStream写入的。我们在使用时需要知道文件中储存的数据类型和储存的顺序,必须根据当时写入文件的顺序来一一对照着读取,不能跳过。在读取String类型时需要用readUTF()...
大家好,这几天想给myeclipse装一个热部署,确遇到一个棘手的问题,安装错版本如何卸载? 1.打开myeclipse-help-install from site .2.选择红框中选项 3.搜索到需要卸载的插件 4.全部选择卸载 结束,已经测试,完全可用...
代码如下: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 模态框简单原型 <button class="btn btn-sm btn-default" data-toggle="modal" data-target="#tees" > 添加</button><div class="modal fade" id="tees" role="dia_bootstrap 原型界面
版权声明: 本文由LeftNotEasy发布于http://leftnoteasy.cnblogs.com, 本文可以被全部的转载或者部分使用,但请注明出处,如果有问题,请联系[email protected]。也可以加我的微博: @leftnoteasy前言: 上一次写了关于PCA与LDA的文章,PCA的实现一般有两种,一种是用特征值分解去实现的,一种是用
&符号这两天要在服务器端一直运行一个Python脚本,当然就想到了在命令后面加&符号$ python /data/python/server.py >python.log &说明:1、 > 表示把标准输出(STDOUT)重定向到 那个文件,这里重定向到了python.log2、 & 表示在后台执行脚本这样可以到达目的,但是,我们退出shell窗口的时候,..._linux 执行python后挂后台
缓冲区溢出基础:#include #include int main(int argc, char *argv[]) { char str[10]; strcpy(str, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); return 0;}这里strcpy的时候,字符长度超出了str所允许的长度,发生了溢出。当我们运行这个代码的时候,程序
本文介绍给下拉框和输入框赋值,代码如下:<!DOCTYPE html><html><head><meta charset="utf-8"><title>select</title></head><body> <label class="w3-label">请求领域<..._jq给select输入框赋值
当一个视频上传到YouTube之后,YouTube会将视频和音频数据保存为不同版本的质量级别。视频分辨率有如下几种:240p、360p、480p、720p、1080p以及Original(原始视频分辨率,是指比1080p大的任意视频尺寸),分辨率越高视频就会越清晰。当增加视频分辨率的时候,YouTube也会对应播放更高质量的音频。下表列出了在Yo...
相关文件: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是面试经常考的知识点,现我从网上转载了一篇文章讲的很精彩,可以参考!转载地址:http://blog.csdn.net/xinjixjz/article/details/6769344有的时候,在脑海中停顿了很久的“显而易见”的东西,其实根本上就是错误的。就拿下面的问题来看: struct T { char ch; int i ; }; 使用size..._sizeof(struct1_s)
本文转载自博客园:https://www.cnblogs.com/soundcode/p/6478004.html1. @Bean:1.1 定义从定义可以看出,@Bean只能用于注解方法和注解的定义。@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTI..._@configuration not bean