文章目录
- 一、登录
- 1.实现登录成功后处理器
- 2.实现登录失败 处理器
- 3.实现未登录直接访问失败 处理器
- 4. 实现从数据库读取user ,实现UserDetailsService.loadUserByUsername
- 5.实现jwt过滤器,重写BasicAuthenticationFilter
- 6.实现Security 的配置类 ,重写WebSecurityConfigurerAdapter
- 二、动态权限管理
- 1. 实现获取当前url的权限标识,重写FilterInvocationSecurityMetadataSource
- 2.实现自定义验证权限,重写AccessDecisionManager
- 3.修改配置类,将重写类添加
- 三、密码加密传输
- 1.前端加密
- 2.后端解密
- 3.修改配置类
一、登录
1.实现登录成功后处理器
@Service("authenticationSuccessHandler")
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private JwtUtils jwtUtils;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response
, Authentication authentication) throws IOException {
logger.info("User: " + request.getParameter("username") + " Login successfully.");
this.returnJson(response,authentication);
}
private void returnJson(HttpServletResponse response,Authentication authentication) throws IOException {
String token = jwtUtils.generateToken(authentication);
response.setStatus(HttpServletResponse.SC_OK);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println("{\"exceptionId\":\"null\",\"messageCode\":\"200\"," +
"\"message\": \"登录成功\",\"Authorization\": \"" + token + "\"}");
}
}
2.实现登录失败 处理器
@Service("authenticationFailHandler")
public class AuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
this.returnJson(response, exception);
}
private void returnJson(HttpServletResponse response,
AuthenticationException exception) throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println("{\"exceptionId\":\"null\",\"messageCode\":\"401\"," +
"\"message\": \"" + "用户名或密码错误" + "\",\"serverTime\": " + System.currentTimeMillis() + "}");
}
}
3.实现未登录直接访问失败 处理器
@Slf4j
@Service("authenticationEntryPointImpl")
class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse response,
AuthenticationException e) throws IOException {
log.error("Spring Securtiy异常", e);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
out.print(JSON.toJSONString(Result.fail(402,"未登录,请先登录!",null)));
}
}
4. 实现从数据库读取user ,实现UserDetailsService.loadUserByUsername
@Component
public class DatabaseUserDetailsService implements UserDetailsService {
@Resource
private TbUserMapper TbUserMapper;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
QueryWrapper<TbUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",userName);
TbUser user = TbUserMapper.selectOne(queryWrapper);
if (user == null) {
throw new UsernameNotFoundException("user + " + userName + "not found.");
}
List<String> roleIdList = TbUserMapper.queryUserOwnedRoleIds(userName);
List<GrantedAuthority> authorities =
roleIdList.stream().map(e -> new SimpleGrantedAuthority(e)).collect(Collectors.toList());
UserDetails userDetails = new org.springframework.security.core.userdetails.User(
user.getUsername(),user.getPassword(),authorities);
return userDetails;
}
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String encode = bCryptPasswordEncoder.encode("1234");
System.out.println(encode);
}
}
5.实现jwt过滤器,重写BasicAuthenticationFilter
@Slf4j
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
JwtUtils jwtUtils = SpringContextUtil.getBean(JwtUtils.class);
String jwt = request.getHeader("Authorization");
if (StrUtil.isBlankOrUndefined(jwt)) {
chain.doFilter(request, response);
return;
}
Claims claim = jwtUtils.getClaimByToken(jwt);
if (claim == null) {
throw new JwtException("token异常!");
}
Date expiration = claim.getExpiration();
String username = claim.getSubject();
List<String> roleIdList = (List<String>) claim.get("roles");
if (jwtUtils.isTokenExpired(expiration)) {
throw new JwtException("token已过期");
}
List<GrantedAuthority> authorities =
roleIdList.stream().map(e -> new SimpleGrantedAuthority(e)).collect(Collectors.toList());
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
= new UsernamePasswordAuthenticationToken(username, null , authorities);
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
chain.doFilter(request, response);
}
}
6.实现Security 的配置类 ,重写WebSecurityConfigurerAdapter
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DatabaseUserDetailsService userDetailsService;
@Autowired
@Qualifier("authenticationSuccessHandler")
private AuthenticationSuccessHandler successHandler;
@Autowired
@Qualifier("authenticationFailHandler")
private AuthenticationFailHandler failHandler;
@Autowired
@Qualifier("authenticationEntryPointImpl")
private AuthenticationEntryPoint entryPoint;
@Autowired
private PasswordEncoder passwordEncoder;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().csrf().disable().cors().and()
.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).authenticated()
.antMatchers("/doc.html", "/webjars/**", "/", "/img.icons/**", "/swagger-resources/**", "/login", "/v2/api-docs").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginProcessingUrl("/login")
.successHandler(successHandler)
.failureHandler(failHandler)
.and().logout().logoutSuccessUrl("/login")
.and().exceptionHandling().authenticationEntryPoint(entryPoint);
http.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
@Bean
public AppFilterInvocationSecurityMetadataSource mySecurityMetadataSource(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) {
AppFilterInvocationSecurityMetadataSource securityMetadataSource = new AppFilterInvocationSecurityMetadataSource(filterInvocationSecurityMetadataSource);
return securityMetadataSource;
}
}
二、动态权限管理
1. 实现获取当前url的权限标识,重写FilterInvocationSecurityMetadataSource
public class AppFilterInvocationSecurityMetadataSource implements org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource {
@Autowired
private TbRoleService tbRoleService;
private FilterInvocationSecurityMetadataSource superMetadataSource;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public AppFilterInvocationSecurityMetadataSource(FilterInvocationSecurityMetadataSource expressionBasedFilterInvocationSecurityMetadataSource){
this.superMetadataSource = expressionBasedFilterInvocationSecurityMetadataSource;
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
List<PathRoleName> pathRoleNameList=tbRoleService.findPathAndRoleName();
for (PathRoleName pathRoleName : pathRoleNameList) {
if (antPathMatcher.match(pathRoleName.getPath(),url)){
return SecurityConfig.createList(pathRoleName.getRoleName());
}
}
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
2.实现自定义验证权限,重写AccessDecisionManager
@Component
public class UrlAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
Iterator<ConfigAttribute> iterator = collection.iterator();
while (iterator.hasNext()) {
ConfigAttribute ca = iterator.next();
String needRole = ca.getAttribute();
if ("ROLE_LOGIN".equals(needRole)) {
if (authentication instanceof AnonymousAuthenticationToken) {
throw new BadCredentialsException("未登录");
} else {
return;
}
}
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("权限不足!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
3.修改配置类,将重写类添加
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().csrf().disable().cors().and()
.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).authenticated()
.antMatchers("/doc.html", "/webjars/**", "/", "/img.icons/**", "/swagger-resources/**", "/login", "/v2/api-docs").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginProcessingUrl("/login")
.successHandler(successHandler)
.failureHandler(failHandler)
.and().logout().logoutSuccessUrl("/login")
.and().exceptionHandling().authenticationEntryPoint(entryPoint)
.and().authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setSecurityMetadataSource(mySecurityMetadataSource(fsi.getSecurityMetadataSource()));
fsi.setAccessDecisionManager(urlAccessDecisionManager);
return fsi;
}
});
http.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
}
三、密码加密传输
1.前端加密
$.ajax({
type: "post",
url: "/login",
data: {
"username": username,
"password": encrypt(password)
},
success: function(res) {
}
});
2.后端解密
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SignInPasswordDecryptionFilter extends OncePerRequestFilter {
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", HttpMethod.POST.name());
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (DEFAULT_ANT_PATH_REQUEST_MATCHER.matcher(request).isMatch()) {
filterChain.doFilter(new FormContentRequestWrapper(request), response);
} else {
filterChain.doFilter(request, response);
}
}
private static class FormContentRequestWrapper extends HttpServletRequestWrapper {
public FormContentRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
@Nullable
public String getParameter(String name) {
String queryStringValue = super.getParameter(name);
if (UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY.equals(name)) {
queryStringValue = "123456";
}
return queryStringValue;
}
}
}
3.修改配置类
http.addFilterBefore(new SignInPasswordDecryptionFilter(), UsernamePasswordAuthenticationFilter.class);