SpringBoot結合JWT登錄權限控制的實現
最近項目中使用springboot+jwt實現登錄權限控制,所以在這裡記錄一下防止以後忘記,畢竟好記性不如爛筆頭嘛~。
首先我們需要導入使用到的jwt的包:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.8.0</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.2.0</version> </dependency>
一、準備LoginUser(存放登錄用戶信息) 和JwtUser
LoginUser.java
public class LoginUser { private Integer userId; private String username; private String password; private String role; 生成getter和setter...... }
JwtUser.java
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Collections; public class JwtUser implements UserDetails{ private Integer id; private String username; private String password; private Collection<? extends GrantedAuthority> authorities; public JwtUser(){ } public JwtUser(LoginUser loginUser){ this.id = loginUser.getUserId(); this.username = loginUser.getUsername(); this.password = loginUser.getPassword(); authorities = Collections.signleton(new SimpleGrantedAuthority(loginUser.getRole())); } @Override public Collection<? extends GrantedAuthority> getAuthorities(){ return authorities; } @Override public String getPassword(){ return password; } @Override public String getUsername(){ return username; } //賬號是否未過期 @Override public boolean isAccountNonExpired(){ return true; } //賬號是否未鎖定 @Override public boolean isAccountNonLocked(){ return true } //賬號憑證是否未過期 @Override public boolean isCredentialsNonExpired(){ return true; } @Override public boolean isEnabled(){ return true; } }
二、準備JwtTokenUtils
import com.bean.JwtUser; import io.jsonwebtoken.*; import org.springframework.security.core.userdetails.UserDetails; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtTokenUtils { public static final String TOKEN_HEADER = "Authorization"; public static final String TOKEN_PREFIX = "Bearer "; public static final String SECRET = "jwtsecret"; public static final String ISS = "echisan"; private static final Long EXPIRATION = 60 * 60 * 3;//過期時間3小時 private static final String ROLE = "role"; //創建token public static String createToken(String username, String role, boolean isRememberMe){ Map map = new HashMap(); map.put(ROLE, role); return Jwts.builder() .signWith(SignatureAlgorithm.HS512, SECRET) .setClaims(map) .setIssuer(ISS) .setSubject(username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) .compact(); } //從token中獲取用戶名(此處的token是指去掉前綴之後的) public static String getUserName(String token){ String username; try { username = getTokenBody(token).getSubject(); } catch ( Exception e){ username = null; } return username; } public static String getUserRole(String token){ return (String) getTokenBody(token).get(ROLE); } private static Claims getTokenBody(String token){ Claims claims = null; try{ claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); } catch(ExpiredJwtException e){ e.printStackTrace(); } catch(UnsupportedJwtException e){ e.printStackTrace(); } catch(MalformedJwtException e){ e.printStackTrace(); } catch(SignatureException e){ e.printStackTrace(); } catch(IllegalArgumentException e){ e.printStackTrace(); } } //是否已過期 public static boolean isExpiration(String token){ try{ return getTokenBody(token).getExpiration().before(new Date()); } catch(Exception e){ e.printStackTrace; } return true; } }
三、準備JWTAuthenticationFilter (驗證登錄)、JWTAuthorizationFilter (鑒定權限)和UserDetailsServiceImpl類 (查庫匹配賬號密碼)
1.JWTAuthenticationFilter.java (驗證登錄)
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter{ private AuthenticationManager authenticationManager; public JWTAuthenticationFilter(AuthenticationManager authenticationManager){ this.authenticationManager = authenticationManager; setAuthenticationFailureHandler(new FailHandler());//設置賬號密碼錯誤時的處理方式 } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException{ //從輸入流中獲取登錄的信息 String username = request.getParameter("username"); String password = request.getParameter("password"); return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(username, password, new ArrayList<>()) ); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult ) throws IOException, ServletException{ JwtUser jwtUser = (JwtUser) authResult.getPrincipal(); Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities(); String role = ""; for(GrantedAuthority authority : authorities){ role = authority.getAuthority(); } String token = JwtTokenUtils.createToken(jwtUser.getUsername, role, false); //返回創建成功的token //但是這裡創建的token隻是單純的token,按照jwt的規定, //最後請求的格式應該是 “Bearer token“。 response.addHeader(JwtTokenUtils.TOKEN_HEADER, JwtTokenUtils.TOKEN_PREFIX + token); } //@Override //protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, // AuthenticationException failed) throws IOException, ServletException { // response.getWriter().write("authentication failed, reason: " + failed.getMessage()); //} }
2.JWTAuthorizationFilter.java (鑒定權限)
public class JWTAuthorizationFilter extends BasicAuthenticationFilter{ public JWTAuthorizationFilter(AuthenticationManager authenticationManager){ super(authenticationManager); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER); //如果請求頭中沒有Authorization信息則直接放行瞭 if(tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)){ chain.doFilter(request, response); return; } //如果請求頭中有token,則進行解析,並且設置認證信息 if(!JwtTokenUtils.isExpiration(tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, “”))){ SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader)); } chain.doFilter(request, response); } private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader){ String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, “”); String username = JwtTokenUtils.getUserName(token); if(username != null){ return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>()); } return null; } }
3.UserDetailsServiceImpl.java (查庫匹配賬號密碼)
import org.springframework.security.core.userdetails.UserDetailsService; @Service public class UserDetailsServiceImpl implements UserDetailsService{ @Autowired UserMapper userMapper; public BCryptPasswordEncoder bCryptPasswordEncoder(){ return new BCryptPasswordEncoder(); } @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { LoginUser loginUser = usersMapper.selectByUserAccount(s); loginUser.setPassword(bCryptPasswordEncoder().encode(loginUser.getPassword())); return new JwtUser(loginUser); } }
四、FailHandler(賬號密碼錯誤時的處理方式)
public class FailHandler extends SimpleUrlAuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { String errorMsg = exception.getMessage(); if(exception instanceof BadCredentialsException){ errorMsg = "用戶名或密碼錯誤"; } response.setHeader("content-type", "application/json"); response.getOutputStream().write(JSONObject.fromObject(new AjaxResult(errorMsg, false)).toString().getBytes("utf-8")); } }
五、配置SecurityConfig
這個類裡規定瞭權限的相關信息
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired private UserDetailsService userDetailsService; @Bean public BCryptPasswordEncoder bCryptPasswordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } @Override public void configure(WebSecurity web) throws Exception{ web.ignoring().antMatchers("/static/**"); } @Override protected void configure(HttpSecurity http) throws Exception{ http.cors().and().csrf().disable() .authorizeRequests() .antMatchers(HttpMethod.POST, "/login").permitAll()//都可以訪問 .antMatchers("/").permitAll() .antMatchers("/index.html").permitAll() .antMatchers("/*.js").permitAll() .antMatchers("/*.css").permitAll() .antMatchers("/*.png").permitAll() .antMatchers("/*.svg").permitAll() .antMatchers("/*.woff").permitAll() .antMatchers("/*.ttf").permitAll() .antMatchers("/*.eot").permitAll() .antMatchers("/test/*").permitAll()//對接口的權限控制,表示對於"/test"路徑下的所有接口無需登錄也可以訪問 .antMatchers("/swagger-ui.html", "/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/webjars/**", "/swagger-resources/configuration/ui").permitAll()//表示對swagger頁面放行 .anyRequest().authenticated()//表示其餘所有請求需要登陸之後才能訪問 .and() .addFilter(new JWTAuthenticationFilter(authenticationManager())) .addFilter(new JWTAuthorizationFilter(authenticationManager())) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } @Bean CorsConfigurationSource corsConfigurationSource() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); return source; } }
到此springboot+jwt實現登錄驗證功能就已實現,其實在securityConfig中還可以加入對某個接口的角色權限,因為項目中沒有用到所以這裡並沒有加上,其實也很簡單,隻是把permitAll()換成其他方法,想實現的話請自行百度啦。更多相關SpringBoot JWT登錄權限控制內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- SpringBoot整合Security實現權限控制框架(案例詳解)
- SpringBoot整合Security安全框架實現控制權限
- SpringBoot整合SpringSecurity實現JWT認證的項目實踐
- spring security 自定義Provider 如何實現多種認證
- SpringSecurity OAtu2+JWT實現微服務版本的單點登錄的示例