赞
踩
当搭建好security授权服务器,资源服务器,和单点登录客户端的时候,挺开心的,基本上都是使用注解写的,也没啥多大的变动.
但是在我使用ResponseBodyAdvice包装类统一返回响应请求的时候,问题就来了,再进行统一包装的时候也会将通过授权后返回的token信息进行包装,可是当客户端对返回的response进行解析的时候死活解析不出来,会把返回的信息全部放入到增强内容additionallnformation当中
这就令我很苦恼,也算没吃透security就开始写带来的问题,找了很多方法,但是都没啥意义.
主要我的客户端是使用security提供的默认的配置,都不好进行改动,而自己又不想去另写客户端(这样还要去写单点登录).
解决的方法其实也有
1.配置security中的响应请求进行重写,然后放入到security config配置中(写了,但由于是使用默认配置,报错了 bean冲突)
2.删掉统一包装方法(暂时使用,因为反正是授权,不包装也可以)
3.跟1其实差不多,别使用默认的注解配置,自己去配置客户端,写sso代码,这样就不用局限于默认框架的拘束之下了.
另一个问题,当用户被封禁后isEnabled()方法为false的时候,用户isAccountNonLocked()同时被触发时,这里就会提前返回用户账户被锁定,源码中首先校验的是是否锁定,在AbstractUserDetailsAuthenticationProvider类中有默认的校验方案
这就会让用户接受到不正确的信息,而这正是默认实现的DaoAuthenticationProvider实现的校验策略,所以我们要用自己的AuthenticationProvider,并且去自己实现用户身份校验.
今天又遇到一个问题,监听类收不到广播类推送的登录成功消息,先放出代码.
- /**
- * 用户登录事件监听
- * 如果用户登录成功则在日志进行记录
- * 并且发送邮件告知用户登录,以及登录位置
- * 提醒end user账号被登录
- */
- @Component
- public class UserLoginListener {
-
- @Autowired
- private HttpServletRequest request;
-
- @Autowired
- private EmailUtil emailUtil;
-
- @EventListener(AuthenticationSuccessEvent.class)
- public void userLoginSuccessfulOnApplicationEvent(AuthenticationSuccessEvent event) throws MessagingException {
-
- //根据访问的请求ip查找用户的位置
- //http://whois.pconline.com.cn/?ip=123.91.217.123查询IP地址的地理位置
- String url = "http://whois.pconline.com.cn/?ip=" + this.getIpAddress();
- String result = this.getResult(url);
-
- int data = result.indexOf("位置:");
- int first = data + 3;
- int last = data + 20;
- String geographicLocation = result.substring(first, last).trim();
- System.out.println(geographicLocation);
-
- //拿到登录的用户信息
- Authentication authentication = event.getAuthentication();
- Object principal = authentication.getPrincipal();
- String userName = authentication.getName();
-
- String userEmail = null;
-
- //获取电子邮箱
- if (principal instanceof User) {
- User user = (User) principal;
- userEmail = user.getEmail();
- }
-
- String userRole = null;
-
- String bodyMessage = authentication.getName() + " 您好,您刚刚在 " + geographicLocation + "\n登录,如果这不是您" +
- "本人进行的操作,请赶紧修改密码或者冻结账号";
-
- Set<String> authorizeds = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
- for (String authorized : authorizeds) {
- if (authorized.equals("normal")) {
- userRole = "尊敬的用户 ";
- break;
- } else if (authorized.equals("root")) {
- userRole = "超级管理员管理员SAMA ";
- } else if (authorized.equals("admin")) {
- userRole = "管理员SAMA ";
- } else if (authorized.equals("CLIENT")) {
- userRole = "Sso客户端信息 \n";
- bodyMessage = authentication.getName() + "--> 服务器启动了,Sso客户端正在上线中";
- }
- }
-
- //邮件的提示信息
- String emailMessage = userRole + bodyMessage;
- String email = userEmail;
- //发送邮件
- Thread thread = new Thread(() -> emailUtil.loginUpEmail(userName, emailMessage, email));
- thread.start();
-
- }
监听的是AuthenticationSuccessEvent这个类,这个类很有意思,它的推送是由ProviderManager验证成功后推送的,之前修改了AuthenticationProvider,也没出现什么问题,但是今天使用的时候就开始整活,死活都监听不到消息,估计就是没推送,换其他事件都能监听到,直接打断点到ProviderManager,可以看到.
- private AuthenticationEventPublisher eventPublisher;
-
- ...............
-
- if (result == null && this.parent != null) {
- try {
- parentResult = this.parent.authenticate(authentication);
- result = parentResult;
- } catch (ProviderNotFoundException var12) {
- } catch (AuthenticationException var13) {
- parentException = var13;
- lastException = var13;
- }
- }
-
- if (result != null) {
- if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
- ((CredentialsContainer)result).eraseCredentials();
- }
-
- if (parentResult == null) {
- this.eventPublisher.publishAuthenticationSuccess(result);
- }
-
- return result;
-
-
- //Manager自己实现的推送方法
- private static final class NullEventPublisher implements AuthenticationEventPublisher {
- private NullEventPublisher() {
- }
-
- public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {
- }
-
- public void publishAuthenticationSuccess(Authentication authentication) {
- }
- }
看完就明白了,啥玩意,搞个空的等于就推了空气,所以只需要自己在验证成功后自己手工去推一下就好了.
- **
- * 自定义认证条件
- */
- @Component
- public class UserAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {
-
- protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
- protected final Log logger = LogFactory.getLog(this.getClass());
-
- @Autowired
- private UserService authUserDetailsService;
-
- @Autowired
- private PasswordEncoder passwordEncoder;
-
- @Autowired
- private ApplicationEventPublisher publisher;
-
- /**
- * @Description 认证处理,返回一个Authentication的实现类则代表认证成功,返回null则代表认证失败
- * @Date 2019/7/5 15:19
- * @Version 1.0
- */
- @Override
- public Authentication authenticate(Authentication authentication) throws AuthenticationException {
-
- //判断访问权限是否为空
- if (authentication.getCredentials() == null) {
- this.logger.debug("Failed to authenticate since no credentials provided");
- throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
- }
-
-
- String presentedPassword = authentication.getCredentials().toString();
- //创建UserDetails类
- UserDetails user = authUserDetailsService.loadUserByUsername(authentication.getName());
-
- //判断密码是否正确,如果不正确则抛出异常
- if (!this.passwordEncoder.matches(presentedPassword, user.getPassword())) {
- this.logger.debug("Failed to authenticate since password does not match stored value");
- throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
- }
-
- //判断用户身份
- if (!user.isEnabled()) {
- this.logger.debug("Failed to authenticate since user account is disabled");
- throw new DisabledException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"));
- } else if (!user.isAccountNonLocked()) {
- this.logger.debug("Failed to authenticate since user account is locked");
- throw new LockedException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"));
- } else if (!user.isAccountNonExpired()) {
- this.logger.debug("Failed to authenticate since user account has expired");
- throw new AccountExpiredException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"));
- }
-
- //获取用户权限信息
- Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
-
- /**
- * 一定要记住,如果没收到消息,就要自己实现,因为在ProviderManager里面它不会给你实现,而是使用
- * 自己空的方法去发布,等于放了个屁,然后就接收不到验证成功的信息,所以要自己实现一个
- */
- UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, presentedPassword, authorities);
- publisher.publishEvent(new AuthenticationSuccessEvent(usernamePasswordAuthenticationToken));
- return usernamePasswordAuthenticationToken;
- }
-
- /**
- * @Description 如果该AuthenticationProvider支持传入的Authentication对象,则返回true
- * @Date 2019/7/5 15:18
- * @Version 1.0
- */
- @Override
- public boolean supports(Class<?> aClass) {
- return aClass.equals(UsernamePasswordAuthenticationToken.class);
- }
-
- @Override
- public void setMessageSource(MessageSource messageSource) {
- this.messages = new MessageSourceAccessor(messageSource);
- }
-
-
- }
而且要注意,它接收的参数是Authentication authentication,可别直接就把一开始拿到的就放进去,那个只是身份验证,不能直接传进去,否则只能拿到账号和密码,必须要创建一个包装的方法,同时将其他的信息放入.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。