SpringSecurity 自定义扩展

编程入门 行业动态 更新时间:2024-10-05 19:18:18

SpringSecurity <a href=https://www.elefans.com/category/jswz/34/1771438.html style=自定义扩展"/>

SpringSecurity 自定义扩展

SpringSecurity 自定义扩展

1、定义filter

import com.lzy.wzy.token.MyUsernamePasswordToken;
import com.lzy.wzy.utils.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class MyUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {private final Logger log= LoggerFactory.getLogger(MyUsernamePasswordAuthenticationFilter.class);private String usernameParameter = "username";private String passwordParameter = "password";private SessionRegistry sessionRegistry;private boolean postOnly = true;public MyUsernamePasswordAuthenticationFilter() {super(new AntPathRequestMatcher("/api/doLogin", "POST"));}public MyUsernamePasswordAuthenticationFilter(SessionRegistry sessionRegistry){super(new AntPathRequestMatcher("/api/doLogin", "POST"));this.sessionRegistry=sessionRegistry;}public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());} else {String username = this.obtainUsername(request);username = username != null ? username : "";username = username.trim();String password = this.obtainPassword(request);password = password != null ? password : "";log.info("IpUtil.getIpAddr(request) {}",IpUtil.getIpAddr(request));MyUsernamePasswordToken authRequest = new MyUsernamePasswordToken(username, password);sessionRegistry.registerNewSession(request.getSession().getId(),authRequest.getPrincipal());this.setDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);}}@Nullableprotected String obtainPassword(HttpServletRequest request) {return request.getParameter(this.passwordParameter);}@Nullableprotected String obtainUsername(HttpServletRequest request) {return request.getParameter(this.usernameParameter);}protected void setDetails(HttpServletRequest request, MyUsernamePasswordToken authRequest) {authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));}public void setUsernameParameter(String usernameParameter) {Assert.hasText(usernameParameter, "Username parameter must not be empty or null");this.usernameParameter = usernameParameter;}public void setPasswordParameter(String passwordParameter) {Assert.hasText(passwordParameter, "Password parameter must not be empty or null");this.passwordParameter = passwordParameter;}public void setPostOnly(boolean postOnly) {this.postOnly = postOnly;}public final String getUsernameParameter() {return this.usernameParameter;}public final String getPasswordParameter() {return this.passwordParameter;}
}

2、定义所需的AuthenticationToken

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;import java.util.ArrayList;
import java.util.Collection;public class MyUsernamePasswordToken extends AbstractAuthenticationToken {private static final long serialVersionUID = 540L;private final Object principal;private Object credentials;private String uri;public MyUsernamePasswordToken(Object principal, Object credentials) {super(null);this.principal = principal;this.credentials = credentials;this.setAuthenticated(false);}public MyUsernamePasswordToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;this.credentials = credentials;super.setAuthenticated(true);}public MyUsernamePasswordToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities,String uri) {super(authorities);this.principal = principal;this.credentials = credentials;this.uri=uri;super.setAuthenticated(true);}public String getUri() {return uri;}public Object getCredentials() {return this.credentials;}public Object getPrincipal() {return this.principal;}public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");super.setAuthenticated(false);}public void eraseCredentials() {super.eraseCredentials();this.credentials = null;}

3、定义provider

import com.lzy.wzy.config.MyPasswordEncoder;
import com.lzy.wzy.exception.DepartmentSelectionException;
import com.lzy.wzy.model.UserBean;
import com.lzy.wzy.service.imp.UserServiceImp;
import com.lzy.wzy.token.MyUsernamePasswordToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;@Slf4j
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {@Autowiredprivate UserServiceImp userServiceImp;@Autowiredprivate MyPasswordEncoder myPasswordEncoder;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {String username = authentication.getName();String password = (String) authentication.getCredentials();UserBean user = (UserBean) userServiceImp.loadUserByUsername(username);if (!user.isEnabled()) {throw new DisabledException("该账户已被禁用,请联系管理员");} else if (!user.isAccountNonLocked()) {throw new LockedException("该账号已被锁定");} else if (!user.isAccountNonExpired()) {throw new AccountExpiredException("该账号已过期,请联系管理员");} else if (!user.isCredentialsNonExpired()) {throw new CredentialsExpiredException("该账户的登录凭证已过期,请重新登录");}
//        if (!checkUtils.isPasswordOk(password)) {
//            throw new BadCredentialsException("密码格式有误!");
//        }//验证密码if (!myPasswordEncoder.matches(password, user.getPassword())) {throw new BadCredentialsException("输入密码错误!");}return new MyUsernamePasswordToken(user, password, user.getAuthorities());}@Overridepublic boolean supports(Class<?> authentication) {// 判断 authentication 是不是 AuthenticationToken 的子类或子接口return MyUsernamePasswordToken.class.isAssignableFrom(authentication);}
}

4、定义jwt转换器,以及token生成配置

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;import javax.annotation.Resource;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.List;@Configuration
public class JwtTokenAuthConfig {@Autowiredprivate MyTokenEnhancer myTokenEnhancer;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate RedisConnectionFactory redisConnectionFactory;@Bean(name = "jwtTokenStoreAuth")public TokenStore jwtTokenStoreAuth(){return new JwtTokenStore(jwtAccessTokenConverterAuth());}@Bean("redisTokenStore")public RedisTokenStore redisTokenStore(RedisConnectionFactory redisConnectionFactory){RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);redisTokenStore.setPrefix("auth_token:");return redisTokenStore;}@Bean(name = "jwtAccessTokenConverterAuth")public JwtAccessTokenConverter jwtAccessTokenConverterAuth(){JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();DefaultAccessTokenConverter defaultAccessTokenConverter = new DefaultAccessTokenConverter();DefaultUserAuthenticationConverter userAuthenticationConverter = new DefaultUserAuthenticationConverter();userAuthenticationConverter.setUserDetailsService(userDetailsService);defaultAccessTokenConverter.setUserTokenConverter(userAuthenticationConverter);jwtAccessTokenConverter.setAccessTokenConverter(defaultAccessTokenConverter);//jwtAccessTokenConverter.setSigningKey("wylwzy");ClassPathResource resource = new ClassPathResource("oauth-jwt.jks");KeyStoreKeyFactory keyFactory = new KeyStoreKeyFactory(resource, "wzy666".toCharArray());KeyPair keyPair = keyFactory.getKeyPair("wzy");jwtAccessTokenConverter.setKeyPair(keyPair);return jwtAccessTokenConverter;}/*** 配置令牌管理*/@Beanpublic AuthorizationServerTokenServices authorizationServerTokenServices(ClientDetailsService clientDetailsService) {DefaultTokenServices service = new DefaultTokenServices();service.setClientDetailsService(clientDetailsService);service.setSupportRefreshToken(true);service.setReuseRefreshToken(true);service.setTokenStore(jwtTokenStoreAuth());TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();List<TokenEnhancer> list=new ArrayList<>();list.add(myTokenEnhancer);list.add(jwtAccessTokenConverterAuth());tokenEnhancerChain.setTokenEnhancers(list);service.setTokenEnhancer(tokenEnhancerChain);return service;}}
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;@Configuration
@Slf4j
public class JwtTokenReConfig {@Bean(name = "jwtTokenStoreRe")public TokenStore jwtTokenStoreRe() {return new JwtTokenStore(jwtAccessTokenConverterRe());}@Bean(name = "jwtAccessTokenConverterRe")public JwtAccessTokenConverter jwtAccessTokenConverterRe() {JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();//jwtAccessTokenConverter.setSigningKey("wylwzy");jwtAccessTokenConverter.setVerifierKey(getPublicKey());return jwtAccessTokenConverter;}private String getPublicKey() {ClassPathResource resource = new ClassPathResource("publickey.txt");try (InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());BufferedReader br = new BufferedReader(inputStreamReader)) {return br.lines().collect(Collectors.joining("\n"));} catch (IOException ioe) {return null;}}
}

5、定义认证成功handler

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lzy.wzy.mapper.UserMapper;
import com.lzy.wzy.model.UserBean;
import com.lzy.wzy.result.Response;
import com.lzy.wzy.result.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2mon.OAuth2AccessToken;
import org.springframework.security.oauth2mon.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;@Slf4j
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {private final static String CLIENT_ID="clientapp";private final static String CLIENT_SECRET="$2a$10$nAwPWghTj2lTSG1yT2MXJu8zbIHW23UyG9wh5KGROnztQqFnJpvLS";private final static String GRANT_TYPE="password";private ClientDetails clientDetails;@Autowiredprivate ClientDetailsService clientDetailsService;@Autowiredprivate AuthorizationServerTokenServices authorizationServerTokenServices;@Autowiredprivate UserMapper userMapper;@Autowiredprivate ObjectMapper objectMapper;@Transactional(rollbackFor = Exception.class)@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {log.info("登录成功");if (null == clientDetails) {log.info("查询 client info");clientDetails = clientDetailsService.loadClientByClientId(CLIENT_ID);if (null == clientDetails) {throw new UnapprovedClientAuthenticationException("clientId不存在" + CLIENT_ID);}}if (!CLIENT_SECRET.equals(clientDetails.getClientSecret())){throw new UnapprovedClientAuthenticationException("clientSecret不匹配" + clientDetails.getClientSecret());}else {Map<String, String> map=new HashMap<>();UserBean user = (UserBean) authentication.getPrincipal();map.put("username",user.getUsername());map.put("password",user.getPassword());//更新最后一次登录时间Map<String,Object> userMap=new HashMap<>();userMap.put("userUuid",user.getUserUuid());userMap.put("timeOnline", LocalDateTime.now());userMapper.updateOnlineTime(userMap);TokenRequest tokenRequest = new TokenRequest(map,CLIENT_ID,clientDetails.getScope(),GRANT_TYPE);OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request,authentication);OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
//            System.out.println(accessToken.getValue());String token = authorizationServerTokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(), tokenRequest).getValue();System.out.println(token);response.setCharacterEncoding("utf-8");response.setContentType("application/json; charset=utf-8");PrintWriter writer = response.getWriter();writer.write(objectMapper.writeValueAsString(new Response<>(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getMsg(), accessToken.getValue())));writer.flush();}}
}

6、将自定义的配置加入到springsecurity的配置

import com.lzy.wzy.filter.MyUsernamePasswordAuthenticationFilter;
import com.lzy.wzy.handle.CustomAuthenticationFailureHandler;
import com.lzy.wzy.handle.CustomAuthenticationSuccessHandler;
import com.lzy.wzy.provider.MyAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
import org.springframework.stereotype.Component;@Component
public class MyAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {@Autowiredprivate MyAuthenticationProvider myAuthenticationProvider;@Autowiredprivate CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;@Autowiredprivate CustomAuthenticationFailureHandler customAuthenticationFailureHandler;@Autowiredprivate SessionRegistry sessionRegistry;@Autowiredprivate SessionInformationExpiredStrategy sessionInformationExpiredStrategy;@Overridepublic void configure(HttpSecurity http) throws Exception {MyUsernamePasswordAuthenticationFilter authenticationFilter = new MyUsernamePasswordAuthenticationFilter(sessionRegistry);authenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));authenticationFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);authenticationFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);authenticationFilter.setSessionAuthenticationStrategy(new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry));http.addFilterBefore(new ConcurrentSessionFilter(sessionRegistry, sessionInformationExpiredStrategy), ConcurrentSessionFilter.class);http.authenticationProvider(myAuthenticationProvider).addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class);http.sessionManagement()//只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面.invalidSessionUrl("/index/none/toLogin").maximumSessions(1).sessionRegistry(sessionRegistry).expiredUrl("/index/none/toLogin");}
}

7、将配置应用WebSecurity中

@Override
protected void configure(HttpSecurity http) throws Exception {http.apply(myAuthenticationSecurityConfig);http.cors();http.headers().frameOptions().disable();http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();//允许匿名访问所有接口http.csrf().disable();http.authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/index/none/toLogin").permitAll().and().logout().logoutSuccessHandler(customLogoutSuccessHandler).permitAll();
}

8、tokenenhancer token增强器

import com.lzy.wzy.model.UserBean;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2mon.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2mon.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;@Component
public class MyTokenEnhancer implements TokenEnhancer {@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {Map<String,Object> map=new HashMap<>();UserBean userBean = (UserBean)oAuth2Authentication.getUserAuthentication().getPrincipal();map.put("username",userBean.getUsername());map.put("userUuid",userBean.getUserUuid());map.put("lzy",userBean.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(map);return oAuth2AccessToken;}
}
OAuth2AccessToken oAuth2AccessToken;
try {oAuth2AccessToken = jwtTokenStoreRe.readAccessToken(token);if (oAuth2AccessToken.isExpired()) {log.info("token 已过期");String refresh = request.getHeader("refresh");OAuth2RefreshToken oAuth2RefreshToken = jwtTokenStoreRe.readRefreshToken(refresh);System.out.println(jwtTokenStoreRe.readAccessToken(oAuth2RefreshToken.getValue()));expiredTokenMono(response);return;}Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();

对于bms认证头生成方法是Base64.getEncoder().encodeToString(“xx:xx”.getBytes())

更多推荐

SpringSecurity 自定义扩展

本文发布于:2024-02-27 16:06:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1707279.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:自定义   SpringSecurity

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!