1

83 阅读9分钟

幻灯片 1

幻灯片 2

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>

幻灯片 3

幻灯片 4

<html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> <link href="maxcdn.bootstrapcdn.com/bootstrap/4…" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> <link href="getbootstrap.com/docs/4.0/ex…" rel="stylesheet" crossorigin="anonymous"/> </head> <body> <div class="container"> <form class="form-signin" method="post" action="/login"> <h2 class="form-signin-heading">CLX-Please sign in</h2> <p> <label for="username" class="sr-only">Username</label> <input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus> </p> <p> <label for="password" class="sr-only">Password</label> <input type="password" id="password" name="password" class="form-control" placeholder="Password" required> </p> <input name="_csrf" type="hidden" value="c66eed70-0fde-4120-9f32-1e035ab5f06c" /> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> </form> </body> </html>

幻灯片 5

public class CustomPasswordEncoder implements PasswordEncoder { @Override public String encode(CharSequence charSequence) { return charSequence.toString(); }

@Override public boolean matches(CharSequence charSequence, String s) { return s.equals(charSequence.toString()); } }

幻灯片 6

@Configuration public class CustomWebMvcConfigure implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login.html"); } }

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). withUser("admin").password("admin").roles("ADMIN") .and() .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated() .and().formLogin().loginPage("/login").permitAll() .and().csrf().disable(); } }

幻灯片 7

幻灯片 8

幻灯片 9

幻灯片 10

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

@Autowired CustomAuthentlcationProvider customAuthentlcationProvider;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthentlcationProvider); // auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated() .and().formLogin().loginPage("/login").permitAll() .and().csrf().disable(); } }

幻灯片 11

@Component public class CustomAuthentlcationProvider implements AuthenticationProvider {

@Autowired private CustomUserDetailsService customUserDetailsService;

@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 获取用户名 String username = (String)authentication.getPrincipal(); // 获取密码 String password = (String)authentication.getCredentials();

*// TODO 此处可以进行加密处理

// 后台处理得到的密码* UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); if(!password.equals(userDetails.getPassword())) { throw new BadCredentialsException("用户名和密码不正确,请重新登录"); }

return new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities()); }

@Override public boolean supports(Class<?> aClass) { return true; } }

幻灯片 12

@Component public class CustomUserDetailsService implements UserDetailsService { @Autowired private CustomUserDetails customUserDetails;

@Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { // TODO 从数据库里面查或者从LDAP里面查 customUserDetails**.setUsername(s); customUserDetails.setPassword("test");

return customUserDetails; } }

幻灯片 13

@Component public class CustomUserDetails implements UserDetails, Serializable { private String username; private String password;

@Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; }

@Override public String getPassword() { return this.password; }

@Override public String getUsername() { return this.username; }

public void setUsername(String username) { this.username = username; }

public void setPassword(String password) { this.password = password; }

//-----为了测试,暂时都设置为True,默认false-----Start @Override public boolean isAccountNonExpired() { return true; }

@Override public boolean isAccountNonLocked() { return true; }

@Override public boolean isCredentialsNonExpired() { return true; }

@Override public boolean isEnabled() { return true; } //-----为了测试,暂时都设置为True,默认false-----End }

幻灯片 14

幻灯片 15

幻灯片 16

幻灯片 17

CustomResponseBody:

public class CustomResponseBody implements Serializable { private String status; private String msg; private String result;

public String getStatus() { return status; }

public void setStatus(String status) { this.status = status; }

public String getMsg() { return msg; }

public void setMsg(String msg) { this.msg = msg; }

public String getResult() { return result; }

public void setResult(String result) { this.result = result; } }

---------------------------------------------------------------

CustomAuthenticationEntryPoint

@Component public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("01"); customResponseBody.setMsg("Need Login"); httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

-----------------------------------------------------------------------

CustomSecurityConfigurer

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

@Autowired CustomAuthentlcationProvider customAuthentlcationProvider;

@Autowired CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthentlcationProvider); // auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().authenticationEntryPoint(customAuthenticationEntryPoint) .and() .authorizeRequests().anyRequest().authenticated() .and().formLogin().loginPage("/login").permitAll() .and().csrf().disable(); } }

幻灯片 18

幻灯片 19

@Component public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("02"); customResponseBody.setMsg("Login success"); httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

---------------------------------------------------------------

CustomSecurityConfigurer

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

@Autowired CustomAuthentlcationProvider customAuthentlcationProvider;

@Autowired CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@Autowired CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

@Autowired CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthentlcationProvider); // auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().authenticationEntryPoint(customAuthenticationEntryPoint) .and() .authorizeRequests().anyRequest().authenticated() .and().formLogin().loginPage("/login") .successHandler(customAuthenticationSuccessHandler) .failureHandler(customAuthenticationFailureHandler) .permitAll() .and().csrf().disable(); } }

幻灯片 20

幻灯片 21

@Component public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("03"); customResponseBody.setMsg("Login fail"); httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

--------------------------------------------------------------------------------------

CustomSecurityConfigurer

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

@Autowired CustomAuthentlcationProvider customAuthentlcationProvider;

@Autowired CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@Autowired CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

@Autowired CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthentlcationProvider); // auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().authenticationEntryPoint(customAuthenticationEntryPoint) .and() .authorizeRequests().anyRequest().authenticated() .and().formLogin().loginPage("/login") .successHandler(customAuthenticationSuccessHandler) .failureHandler(customAuthenticationFailureHandler) .permitAll() .and().csrf().disable(); } }

幻灯片 22

幻灯片 23

幻灯片 24

幻灯片 25

CustomUserDetailsService

@Component public class CustomUserDetailsService implements UserDetailsService { @Autowired private CustomUserDetails customUserDetails;

@Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { // TODO 从数据库里面查或者从LDAP里面查 customUserDetails**.setUsername(s); customUserDetails.setPassword("test");

Set authoritiesSet = new HashSet(); GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_AAA"); authoritiesSet.add(grantedAuthority); customUserDetails.setAuthorities(authoritiesSet);

return customUserDetails; } }

-------------------------------------------------------------

CustomUserDetails

@Component public class CustomUserDetails implements UserDetails, Serializable { private String username; private String password; // 权限 private Set<? extends GrantedAuthority> authorities;

@Override public Collection<? extends GrantedAuthority> getAuthorities() { return this.authorities; }

public void setAuthorities(Set<? extends GrantedAuthority> authorities) { this.authorities = authorities; }

@Override public String getPassword() { return this.password; }

@Override public String getUsername() { return this.username; }

public void setUsername(String username) { this.username = username; }

public void setPassword(String password) { this.password = password; }

//-----为了测试,暂时都设置为True,默认false-----Start @Override public boolean isAccountNonExpired() { return true; }

@Override public boolean isAccountNonLocked() { return true; }

@Override public boolean isCredentialsNonExpired() { return true; }

@Override public boolean isEnabled() { return true; } //-----为了测试,暂时都设置为True,默认false-----End }

幻灯片 26

幻灯片 27

@Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("04"); customResponseBody.setMsg("Login Access Denied"); httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

-----------------------------------------------------------------------------------

http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);

幻灯片 28

幻灯片 29

@Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("04"); customResponseBody.setMsg("Login Access Denied"); httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

-----------------------------------------------------------------------------------

http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);

幻灯片 30

CustomAuthentlcationProvider:

@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 获取用户名 String username = (String)authentication.getPrincipal(); // 获取密码 String password = (String)authentication.getCredentials();

*// TODO 此处可以进行加密处理

// 后台处理得到的密码* UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); if(!password.equals(userDetails.getPassword())) { throw new BadCredentialsException("用户名和密码不正确,请重新登录"); }

// return new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities()); return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities()); }

--------------------------------------------------------------------------------------------------

RbacService.java

@Component("rbacservice") public class RbacService { public boolean hasPermission(HttpServletRequest request, Authentication authentication) { Object userInfo = authentication.getPrincipal(); boolean hasPermission = false;

if (userInfo instanceof CustomUserDetails) { String username = ((CustomUserDetails) userInfo).getUsername();

// TODO 从数据库获取资源 Set urls = new HashSet<>(); // urls.add("/clxTest1.html"); urls.add("/clxTest.html"); Set set2 = new HashSet<>(); Set set3 = new HashSet<>();

AntPathMatcher antPathMatcher = new AntPathMatcher();

for (String url : urls) { if (antPathMatcher.match(url, request.getRequestURI())) { hasPermission = true; break; } } return hasPermission; } else { return false; }

} }

-----------------------------------------------------------------------------------------------

CustomSecurityConfigurer

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

@Autowired CustomAuthentlcationProvider customAuthentlcationProvider;

@Autowired CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@Autowired CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

@Autowired CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

@Autowired CustomAccessDeniedHandler customAccessDeniedHandler;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthentlcationProvider); // auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().authenticationEntryPoint(customAuthenticationEntryPoint) .and() .authorizeRequests() .antMatchers("/**/webjars/**").permitAll() .antMatchers("/**/swagger-ui.html").permitAll() .antMatchers("/**/swagger-resources/**").permitAll() .antMatchers("/**/swagger-ui/**").permitAll() .antMatchers("/**/v2/api-docs").permitAll()

// 这种方式代码中会写死 // .antMatchers("/clxTest.html").hasRole("AAA")

.anyRequest() .access("@rbacservice.hasPermission(request, authentication)") // .authenticated() .and().formLogin().loginPage("/login") .successHandler(customAuthenticationSuccessHandler) .failureHandler(customAuthenticationFailureHandler) .permitAll() .and().csrf().disable();

http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler); } }

幻灯片 31

Login.html 加入remember-me:

<html lang="en" xmlns:c="www.w3.org/1999/XSL/Tr… <head> <meta charset="UTF-8"> <title>Login</title> <link href="maxcdn.bootstrapcdn.com/bootstrap/4…" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> <link href="getbootstrap.com/docs/4.0/ex…" rel="stylesheet" crossorigin="anonymous"/> </head> <body> <div class="container"> <form class="form-signin" method="post" action="/login"> <h2 class="form-signin-heading">CLX-Please sign in</h2> <p> <label for="username" class="sr-only">Username</label> <input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus> </p> <p> <label for="password" class="sr-only">Password</label> <input type="password" id="password" name="password" class="form-control" placeholder="Password" required> </p> <p> 自动登录:    **<input type="checkbox" name="remember-me", id="remember-me" checked="checked"/> </p> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> </form> </div> </body> </html>

-----------------------------------------------------------------------------

CustomSecurityConfigurer中配置remember me,注入userDetailsService

// 设置remember me http.rememberMe().rememberMeParameter("remember-me").userDetailsService(customUserDetailsService).tokenValiditySeconds(300);

幻灯片 32

幻灯片 33

自定义logout页面,CustomLogoutSuccessHandler

/** * Description * * @author chiliangxu * @date 2018/12/25 12:59 */ @Component public class CustomLogoutSuccessHandler implements LogoutSuccessHandler { @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("05"); customResponseBody.setMsg("Logout Success"); httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

-------------------------------------------------------------------------------------

在CustomSecurityConfigurer中注入CustomLogoutSuccessHandler

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

@Autowired CustomAuthentlcationProvider customAuthentlcationProvider;

@Autowired CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@Autowired CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

@Autowired CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

@Autowired CustomAccessDeniedHandler customAccessDeniedHandler;

@Autowired CustomUserDetailsService customUserDetailsService;

@Autowired CustomLogoutSuccessHandler customLogoutSuccessHandler;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthentlcationProvider); // auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().authenticationEntryPoint(customAuthenticationEntryPoint) .and() .authorizeRequests() .antMatchers("/**/webjars/**").permitAll() .antMatchers("/**/swagger-ui.html").permitAll() .antMatchers("/**/swagger-resources/**").permitAll() .antMatchers("/**/swagger-ui/**").permitAll() .antMatchers("/**/v2/api-docs").permitAll()

// 这种方式代码中会写死 // .antMatchers("/clxTest.html").hasRole("AAA")

.anyRequest() .access("@rbacservice.hasPermission(request, authentication)") // .authenticated() .and().formLogin().loginPage("/login") .successHandler(customAuthenticationSuccessHandler) .failureHandler(customAuthenticationFailureHandler) .permitAll() .and() .logout() .logoutSuccessHandler(customLogoutSuccessHandler) .permitAll() .and() .csrf().disable();

// 设置无权限访问内容 http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler); // 设置remember me http.rememberMe().rememberMeParameter("remember-me").userDetailsService(customUserDetailsService).tokenValiditySeconds(300); } }

幻灯片 34

现在貌似大多数网站用户认证都是基于 session 的,即在服务端生成用户相关的 session 数据,而发给客户端 sesssion_id 存放到 cookie 中,这样用客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户认证。这种认证方式,可以更好的在服务端对会话进行控制,安全性比较高(session_id 随机),但是服务端需要存储 session 数据(如内存或数据库),这样无疑增加维护成本和减弱可扩展性(多台服务器)。 CSRF 攻击一般基于 cookie 。另外,如果是原生 app 使用这种服务接口,又因为没有浏览器 cookie 功能,所以接入会相对麻烦。

基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用户验证后,服务端生成一个 token(hash 或 encrypt)发给客户端,客户端可以放到 cookie 或 localStorage 中,每次请求时在 Header 中带上 token ,服务端收到 token 通过验证后即可确认用户身份。这种方式相对 cookie 的认证方式就简单一些,服务端不用存储认证数据,易维护扩展性强, token 存在 localStorage 可避免 CSRF , web 和 app 应用这用接口都比较简单。不过这种方式在加密或解密的时候会有一些性能开销(好像也不是很大),有些对称加密存在安全隐患(aes cbc 字节翻转攻击)。

幻灯片 35

幻灯片 36

<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>

--------------------------------------------------------

JwtTokenUtil

package com.clx.springbootdemoclx.config.security.utils;

import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

/** * JwtTokenUtil * * @author chiliangxu * @date 2018/12/25 15:10 */ public class JwtTokenUtil {

/** * * Description: * * @param: [subject, expiration, salt] 用户名,过期时间(s),私钥 * @return: java.lang.String * @auther: chiliangxu * @date: 2018/12/25 15:19 */ public static String generateToken(String subject, int expirationSeconds, String salt) { return Jwts.builder() .setClaims(null) .setSubject(subject) .setExpiration(new Date(System.currentTimeMillis() + expirationSeconds * 1000)) .signWith(SignatureAlgorithm.HS512, salt) .compact(); }

/** * * Description: * * @param: [token, salt] token,私钥 * @return: java.lang.String * @auther: chiliangxu * @date: 2018/12/25 15:27 */ public static String parseToken(String token, String salt) { String subject = null; try { Claims claims = Jwts.parser().setSigningKey(salt).parseClaimsJws(token).getBody(); subject = claims.getSubject(); } catch(Exception e) {

} return subject; }

/** * * Description: Test * * @param: [args] * @return: void * @auther: chiliangxu * @date: 2018/12/25 15:27 */ public static void main(String[] args) { String token = generateToken("clx", 10, "_secret"); String subject = parseToken(token, "_secret"); System.out.println(token); System.out.println(subject); } }

幻灯片 37

JwtAuthenticationTokenFilter

package com.clx.springbootdemoclx.config.security;

import com.clx.springbootdemoclx.config.security.utils.JwtTokenUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;

/** * Description * * @author chiliangxu * @date 2018/12/25 15:44 */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired CustomUserDetailsService customUserDetailsService;

@Value("${jwt.header:#{null}}") private String jwtHeader;

@Value("${jwt.secret:#{null}}") private String jwtsecret;

@Value("${jwt.tokenHead:#{null}}") private String jwtTokenHead;

@Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { // 可在yml中配置 String authHeader = httpServletRequest.getHeader(jwtHeader);

if (!StringUtils.isEmpty(authHeader) && authHeader.startsWith(jwtTokenHead + " ")) { final String authToken = authHeader.substring((jwtTokenHead + " ").length()); String username = JwtTokenUtil.parseToken(authToken, jwtsecret);

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);

if (userDetails != null) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); SecurityContextHolder.getContext().setAuthentication(authentication); } } } filterChain.doFilter(httpServletRequest, httpServletResponse); } }

-----------------------------------------------------------------------------

CustomAuthenticationSuccessHandler

@Component public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

@Value("${jwt.expiration:#{null}}") private int jwtExpirationSeconds;

@Value("${jwt.secret:#{null}}") private String jwtsecret;

@Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("02"); customResponseBody.setMsg("Login success");

// 产生token CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); String token = JwtTokenUtil.generateToken(userDetails.getUsername(), jwtExpirationSeconds, jwtsecret); customResponseBody.setToken(token);

httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

-----------------------------------------------------------------------------------------------------

CustomSecurityConfigurer

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

// @Autowired // CustomAuthentlcationProvider customAuthentlcationProvider;

@Autowired CustomAuthenticationEntryPointHandler customAuthenticationEntryPointHandler;

@Autowired CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

@Autowired CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

@Autowired CustomAccessDeniedHandler customAccessDeniedHandler;

@Autowired CustomUserDetailsService customUserDetailsService;

@Autowired CustomLogoutSuccessHandler customLogoutSuccessHandler;

@Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // JWT时不用Provider方式 // auth.authenticationProvider(customAuthentlcationProvider); // 这种加密算法每次产生结果都不一样,md5已经有点过时了 auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());

// auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .httpBasic().authenticationEntryPoint(customAuthenticationEntryPointHandler) .and() .authorizeRequests() .antMatchers("/**/webjars/**").permitAll() .antMatchers("/**/swagger-ui.html").permitAll() .antMatchers("/**/swagger-resources/**").permitAll() .antMatchers("/**/swagger-ui/**").permitAll() .antMatchers("/**/v2/api-docs").permitAll()

// 这种方式代码中会写死 // .antMatchers("/clxTest.html").hasRole("AAA")

.anyRequest() .access("@rbacservice.hasPermission(request, authentication)") // .authenticated() .and().formLogin().loginPage("/login") .successHandler(customAuthenticationSuccessHandler) .failureHandler(customAuthenticationFailureHandler) .permitAll() .and() .logout() .logoutSuccessHandler(customLogoutSuccessHandler) .permitAll() .and() .csrf().disable();

// 设置无权限访问内容 http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler); // 设置remember me http.rememberMe().rememberMeParameter("remember-me").userDetailsService(customUserDetailsService).tokenValiditySeconds(300);

// 在默认fileter UsernamePasswordAuthenticationFilter执行前加入JwtTokenFilter http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

// 禁用header缓存 http.headers().cacheControl(); } }

幻灯片 38

JwtAuthenticationTokenFilter

package com.clx.springbootdemoclx.config.security;

import com.clx.springbootdemoclx.config.security.utils.JwtTokenUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;

/** * Description * * @author chiliangxu * @date 2018/12/25 15:44 */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired CustomUserDetailsService customUserDetailsService;

@Value("${jwt.header:#{null}}") private String jwtHeader;

@Value("${jwt.secret:#{null}}") private String jwtsecret;

@Value("${jwt.tokenHead:#{null}}") private String jwtTokenHead;

@Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { // 可在yml中配置 String authHeader = httpServletRequest.getHeader(jwtHeader);

if (!StringUtils.isEmpty(authHeader) && authHeader.startsWith(jwtTokenHead + " ")) { final String authToken = authHeader.substring((jwtTokenHead + " ").length()); String username = JwtTokenUtil.parseToken(authToken, jwtsecret);

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);

if (userDetails != null) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); SecurityContextHolder.getContext().setAuthentication(authentication); } } } filterChain.doFilter(httpServletRequest, httpServletResponse); } }

-----------------------------------------------------------------------------

CustomAuthenticationSuccessHandler

@Component public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

@Value("${jwt.expiration:#{null}}") private int jwtExpirationSeconds;

@Value("${jwt.secret:#{null}}") private String jwtsecret;

@Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("02"); customResponseBody.setMsg("Login success");

// 产生token CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); String token = JwtTokenUtil.generateToken(userDetails.getUsername(), jwtExpirationSeconds, jwtsecret); customResponseBody.setToken(token);

httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

-----------------------------------------------------------------------------------------------------

CustomSecurityConfigurer

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

// @Autowired // CustomAuthentlcationProvider customAuthentlcationProvider;

@Autowired CustomAuthenticationEntryPointHandler customAuthenticationEntryPointHandler;

@Autowired CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

@Autowired CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

@Autowired CustomAccessDeniedHandler customAccessDeniedHandler;

@Autowired CustomUserDetailsService customUserDetailsService;

@Autowired CustomLogoutSuccessHandler customLogoutSuccessHandler;

@Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // JWT时不用Provider方式 // auth.authenticationProvider(customAuthentlcationProvider); // 这种加密算法每次产生结果都不一样,md5已经有点过时了 auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());

// auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .httpBasic().authenticationEntryPoint(customAuthenticationEntryPointHandler) .and() .authorizeRequests() .antMatchers("/**/webjars/**").permitAll() .antMatchers("/**/swagger-ui.html").permitAll() .antMatchers("/**/swagger-resources/**").permitAll() .antMatchers("/**/swagger-ui/**").permitAll() .antMatchers("/**/v2/api-docs").permitAll()

// 这种方式代码中会写死 // .antMatchers("/clxTest.html").hasRole("AAA")

.anyRequest() .access("@rbacservice.hasPermission(request, authentication)") // .authenticated() .and().formLogin().loginPage("/login") .successHandler(customAuthenticationSuccessHandler) .failureHandler(customAuthenticationFailureHandler) .permitAll() .and() .logout() .logoutSuccessHandler(customLogoutSuccessHandler) .permitAll() .and() .csrf().disable();

// 设置无权限访问内容 http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler); // 设置remember me http.rememberMe().rememberMeParameter("remember-me").userDetailsService(customUserDetailsService).tokenValiditySeconds(300);

// 在默认fileter UsernamePasswordAuthenticationFilter执行前加入JwtTokenFilter http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

// 禁用header缓存 http.headers().cacheControl(); } }

幻灯片 39

JwtAuthenticationTokenFilter

package com.clx.springbootdemoclx.config.security;

import com.clx.springbootdemoclx.config.security.utils.JwtTokenUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;

/** * Description * * @author chiliangxu * @date 2018/12/25 15:44 */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired CustomUserDetailsService customUserDetailsService;

@Value("${jwt.header:#{null}}") private String jwtHeader;

@Value("${jwt.secret:#{null}}") private String jwtsecret;

@Value("${jwt.tokenHead:#{null}}") private String jwtTokenHead;

@Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { // 可在yml中配置 String authHeader = httpServletRequest.getHeader(jwtHeader);

if (!StringUtils.isEmpty(authHeader) && authHeader.startsWith(jwtTokenHead + " ")) { final String authToken = authHeader.substring((jwtTokenHead + " ").length()); String username = JwtTokenUtil.parseToken(authToken, jwtsecret);

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);

if (userDetails != null) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); SecurityContextHolder.getContext().setAuthentication(authentication); } } } filterChain.doFilter(httpServletRequest, httpServletResponse); } }

-----------------------------------------------------------------------------

CustomAuthenticationSuccessHandler

@Component public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

@Value("${jwt.expiration:#{null}}") private int jwtExpirationSeconds;

@Value("${jwt.secret:#{null}}") private String jwtsecret;

@Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { CustomResponseBody customResponseBody = new CustomResponseBody(); customResponseBody.setStatus("02"); customResponseBody.setMsg("Login success");

// 产生token CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); String token = JwtTokenUtil.generateToken(userDetails.getUsername(), jwtExpirationSeconds, jwtsecret); customResponseBody.setToken(token);

httpServletResponse.getWriter().write(JSON.toJSONString(customResponseBody)); } }

-----------------------------------------------------------------------------------------------------

CustomSecurityConfigurer

@Configuration public class CustomSecurityConfigurer extends WebSecurityConfigurerAdapter {

// @Autowired // CustomAuthentlcationProvider customAuthentlcationProvider;

@Autowired CustomAuthenticationEntryPointHandler customAuthenticationEntryPointHandler;

@Autowired CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

@Autowired CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

@Autowired CustomAccessDeniedHandler customAccessDeniedHandler;

@Autowired CustomUserDetailsService customUserDetailsService;

@Autowired CustomLogoutSuccessHandler customLogoutSuccessHandler;

@Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // JWT时不用Provider方式 // auth.authenticationProvider(customAuthentlcationProvider); // 这种加密算法每次产生结果都不一样,md5已经有点过时了 auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());

// auth.inMemoryAuthentication().passwordEncoder(new CustomPasswordEncoder()). // withUser("admin").password("admin").roles("ADMIN") // .and() // .withUser("clx").password("clx").roles("USER"); }

@Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .httpBasic().authenticationEntryPoint(customAuthenticationEntryPointHandler) .and() .authorizeRequests() .antMatchers("/**/webjars/**").permitAll() .antMatchers("/**/swagger-ui.html").permitAll() .antMatchers("/**/swagger-resources/**").permitAll() .antMatchers("/**/swagger-ui/**").permitAll() .antMatchers("/**/v2/api-docs").permitAll()

// 这种方式代码中会写死 // .antMatchers("/clxTest.html").hasRole("AAA")

.anyRequest() .access("@rbacservice.hasPermission(request, authentication)") // .authenticated() .and().formLogin().loginPage("/login") .successHandler(customAuthenticationSuccessHandler) .failureHandler(customAuthenticationFailureHandler) .permitAll() .and() .logout() .logoutSuccessHandler(customLogoutSuccessHandler) .permitAll() .and() .csrf().disable();

// 设置无权限访问内容 http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler); // 设置remember me http.rememberMe().rememberMeParameter("remember-me").userDetailsService(customUserDetailsService).tokenValiditySeconds(300);

// 在默认fileter UsernamePasswordAuthenticationFilter执行前加入JwtTokenFilter http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

// 禁用header缓存 http.headers().cacheControl(); } }

幻灯片 40