SpringSecurity+JWT使用步骤及心得

1、添加相关依赖

主要是springsecurity和jwt依赖



4.0.0
org.springframework.bootspring-boot-starter-parent2.6.4 

com.njwj
gx
0.0.1-SNAPSHOT
gx
guanxian
1.8UTF-8UTF-8

org.springframework.bootspring-boot-starter-weborg.mybatis.spring.bootmybatis-spring-boot-starter2.2.2com.fasterxml.jackson.corejackson-corecom.fasterxml.jackson.corejackson-databindcom.fasterxml.jackson.corejackson-annotationsorg.springframework.bootspring-boot-starter-securityio.jsonwebtokenjjwt0.9.0com.oracle.database.jdbcojdbc8runtimeorg.springframework.bootspring-boot-starter-testtesttk.mybatismapper-spring-boot-starter2.0.4org.projectlomboklombokprovided
org.springframework.bootspring-boot-maven-plugin2.6.4


2、配置数据库和myabtis

这里就不一一赘述了

3、User类、UserMapper、UserService、UserServiceImpl

3.1 User类

@Data
@Table(name = "USER_S")
public class User implements Serializable {private static final long serialVersionUID = 2L;@Id@Column(name = "USER_S_ID")private Integer userId;@Column(name = "USER_S_NAME")private String userName;@Column(name = "USER_S_PASS")private String userPassword;@Column(name = "USER_S_CODE")private String userCode;@Column(name = "USER_S_SEX")private String userSex;@Column(name = "USER_S_PHONE")private String userPhone;@Column(name = "USER_S_STATUS")private String userStatus;@Column(name = "USER_S_IN")private Date userIn;@Column(name = "USER_S_SKILL")private String userSkill;@Column(name = "USER_S_LEVEL")private String userLevel;@Column(name = "USER_S_W_STATUS")private String userWStatus;
}

3.2 UserMapper

这里使用了通用Mapper

@Mapper
public interface UserMapper extends tk.mybatis.mapper.common.Mapper {}

3.3 UserService

public interface UserService {Map addUser(User user);User findByUserName(String username);List findAll();Map deleteUser(Integer id);
}

3.4 UserServiceImpl

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic Map addUser(User user) {Map addMap = new HashMap<>();if (userMapper.insert(user)>0) {addMap.put("result", "true");addMap.put("msg", "添加成功");} else {addMap.put("result", "false");addMap.put("msg", "添加失败");}return addMap;}@Overridepublic User findByUserName(String username) {User user = new User();user.setUserName(username);return userMapper.selectOne(user);}@Overridepublic List findAll() {return userMapper.selectAll();}@Overridepublic Map deleteUser(Integer id) {Map delMap = new HashMap<>();if (userMapper.deleteByPrimaryKey(id)>0) {delMap.put("result", "true");delMap.put("msg", "删除成功");} else {delMap.put("result", "false");delMap.put("msg", "删除失败");}return delMap;}
}

4、创建JWT工具类

用于管理token操作

public class JWTUtil {public static final String TOKEN_HEADER = "Authorization";public static final String TOKEN_PREFIX = "Bearer";// TOKEN 过期时间public static final long EXPIRATION = 1000 * 60 * 30; // 三十分钟public static final String APP_SECRET_KEY = "secret";private static final String ROLE_CLAIMS = "rol";/*** 生成token** @param username* @param role* @return*/public static String createToken(String username, String role) {Map map = new HashMap<>();map.put(ROLE_CLAIMS, role);String token = Jwts.builder().setSubject(username).setClaims(map).claim("username", username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)).signWith(SignatureAlgorithm.HS256, APP_SECRET_KEY).compact();return token;}/*** 获取当前登录用户用户名** @param token* @return*/public static String getUsername(String token) {Claims claims = Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody();return claims.get("username").toString();}/*** 获取当前登录用户角色** @param token* @return*/public static String getUserRole(String token) {Claims claims = Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody();return claims.get("rol").toString();}/*** 解析token中的信息** @param token* @return*/public static Claims checkJWT(String token) {try {final Claims claims = Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody();return claims;} catch (Exception e) {e.printStackTrace();return null;}}/*** 检查token是否过期** @param token* @return*/public static boolean isExpiration(String token) {Claims claims = Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody();return claims.getExpiration().before(new Date());}
}

5、创建JwtUser类

主要用于封装登录用户相关信息,例如用户名,密码,权限集合等,必须实现UserDetails接口

public class JWTUser implements UserDetails {private Integer userId;private String userName;private String userPassword;private Collection authorities;public JWTUser(){}public JWTUser(User user) {userId = user.getUserId();userName = user.getUserName();userPassword = user.getUserPassword();authorities = Collections.singleton(new SimpleGrantedAuthority(user.getUserLevel()));}public Collection getAuthorities() {return authorities;}public String getPassword() {return userPassword;}public String getUsername() {return userName;}public boolean isAccountNonExpired() {return true;}public boolean isAccountNonLocked() {return true;}public boolean isCredentialsNonExpired() {return true;}public boolean isEnabled() {return true;}
}

6、创建JwtUserService

必须实现UserDetailsService,重写loadUserByUsername()方法

@Service
public class JWTUserService implements UserDetailsService {@Autowiredprivate UserService userService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userService.findByUserName(username);if (user != null){JWTUser jwtUser = new JWTUser(user);return jwtUser;}else {try {throw new ValidationException("该用户不存在");}catch (ValidationException e){e.printStackTrace();}}return null;}
}

7、配置认证拦截器

拦截用户登录信息

/**** 验证用户名密码正确后,生成一个token,并将token返回给客户端* 该类继承自UsernamePasswordAuthenticationFilter,重写了其中的2个方法* attemptAuthentication:接收并解析用户凭证。* successfulAuthentication:用户成功登录后,这个方法会被调用,我们在这个方法里生成token并返回。**/
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {private AuthenticationManager authenticationManager;/*** 拦截登录请求* @param authenticationManager*/public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {this.authenticationManager = authenticationManager;// super.setFilterProcessesUrl("/login");}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {// 从输入流中获取到登录的信息try {User loginUser = new ObjectMapper().readValue(request.getInputStream(), User.class);return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginUser.getUserName(), loginUser.getUserPassword()));} catch (IOException e) {e.printStackTrace();return null;}}// 成功验证后调用的方法// 如果验证成功,就生成token并返回@Overrideprotected void successfulAuthentication(HttpServletRequest request,HttpServletResponse response,FilterChain chain,Authentication authResult) throws IOException, ServletException {JWTUser jwtUser = (JWTUser) authResult.getPrincipal();String role = "";Collection authorities = jwtUser.getAuthorities();for (GrantedAuthority authority : authorities) {role = authority.getAuthority();}//生成tokenString token = JWTUtil.createToken(jwtUser.getUsername(), role);// 返回创建成功的token  但是这里创建的token只是单纯的token// 按照jwt的规定,最后请求的时候应该是 `Bearer token`response.setCharacterEncoding("UTF-8");response.setContentType("application/json; charset=utf-8");String tokenStr = JWTUtil.TOKEN_PREFIX + token;response.setHeader("token", tokenStr);ResultInfo resultInfo = new ResultInfo();response.setStatus(200);PrintWriter writer = response.getWriter();resultInfo.setResult(true);resultInfo.setMsg("登录成功");ObjectMapper mapper = new ObjectMapper();String resultJson = mapper.writeValueAsString(resultInfo);writer.write(resultJson);writer.flush();writer.close();}// 失败 返回错误就行@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {response.setCharacterEncoding("UTF-8");response.setStatus(401);PrintWriter writer = response.getWriter();ResultInfo resultInfo = new ResultInfo();resultInfo.setResult(false);resultInfo.setMsg("用户名或密码不正确,请重新登录");ObjectMapper mapper = new ObjectMapper();String resultJson = mapper.writeValueAsString(resultInfo);writer.write(resultJson);writer.flush();writer.close();}
}

8、配置权限拦截器

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {super(authenticationManager);}@SneakyThrows@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws IOException, ServletException {String tokenHeader = request.getHeader(JWTUtil.TOKEN_HEADER);// 如果请求头中没有Authorization信息则直接放行了if (tokenHeader == null || !tokenHeader.startsWith(JWTUtil.TOKEN_PREFIX)) {chain.doFilter(request, response);return;}// 如果请求头中有token,则进行解析,并且设置认证信息SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));super.doFilterInternal(request, response, chain);}// 这里从token中获取用户信息并新建一个token 就是上面说的设置认证信息private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) throws Exception {String token = tokenHeader.replace(JWTUtil.TOKEN_PREFIX, "");// 检测token是否过期 如果过期会自动抛出错误JWTUtil.isExpiration(token);String username = JWTUtil.getUsername(token);String role = JWTUtil.getUserRole(token);if (username != null) {return new UsernamePasswordAuthenticationToken(username, null,Collections.singleton(new SimpleGrantedAuthority(role)));}return null;}
}

9、配置SecurityConfig文件

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate  JWTUserService jwtUserService;@Autowiredprivate UrlLogoutSuccessHandler logoutSuccessHandler;@Autowiredprivate UrlAccessDeniedHandler accessDeniedHandler;@Autowiredprivate UrlAuthenticationEntryPoint authenticationEntryPoint;@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}/*** 这边 通过重写configure(),去数据库查询用户是否存在* @param auth* @throws Exception*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(jwtUserService).passwordEncoder(bCryptPasswordEncoder());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().and().csrf().disable().httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().formLogin().loginProcessingUrl("/login").and().addFilter(new JWTAuthenticationFilter(authenticationManager())) // 用户登录拦截.addFilter(new JWTAuthorizationFilter(authenticationManager()))  // 权限拦截// 不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 以/user 开头的请求 都需要进行验证.antMatchers("/user/**").authenticated()// 其他都放行了.anyRequest().permitAll()//.anyRequest().authenticated().and().logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler).clearAuthentication(true).and().exceptionHandling().accessDeniedHandler(accessDeniedHandler);}@BeanCorsConfigurationSource corsConfigurationSource() {final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());return source;}
}

10、UserController

@Controller
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate BCryptPasswordEncoder bCryptPasswordEncoder;@PostMapping(value = "/addUser")public @ResponseBody ResultInfo addUser(@RequestBody User user){user.setUserPassword(bCryptPasswordEncoder.encode(user.getUserPassword()));Map resultMap = userService.addUser(user);ResultInfo resultInfo = new ResultInfo<>();if (Boolean.valueOf(resultMap.get("result"))){resultInfo.setResult(true);resultInfo.setMsg(resultMap.get("msg"));}else{resultInfo.setResult(false);resultInfo.setMsg(resultMap.get("msg"));}return resultInfo;}@GetMappingpublic User findByUserName(String userName){return userService.findByUserName(userName);}@GetMapping("/findAll")@ResponseBody@PreAuthorize("hasAnyAuthority('admin')")public ResultInfo findAll(){ResultInfo resultInfo = new ResultInfo<>();List list = userService.findAll();if (list!=null&&list.size()>0){resultInfo.setResult(true);resultInfo.setMsg("success");resultInfo.setList(list);}else{resultInfo.setResult(false);resultInfo.setMsg("failure");resultInfo.setList(null);}return resultInfo;}@GetMapping("/delUser")public @ResponseBody ResultInfo delUser(@RequestParam Integer id){Map resultMap = userService.deleteUser(id);ResultInfo delResultInfo = new ResultInfo<>();if (Boolean.valueOf(resultMap.get("result"))){delResultInfo.setResult(true);delResultInfo.setMsg(resultMap.get("msg"));}else{delResultInfo.setResult(false);delResultInfo.setMsg(resultMap.get("msg"));}return delResultInfo;}
}

11、最后配置handler

主要考虑到前后端分离,传递给前端json字符串
11.1 无权访问的AccessDeniedHandler

@Component
public class UrlAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {ResultInfo resultInfo = new ResultInfo();response.setCharacterEncoding("UTF-8");PrintWriter writer = response.getWriter();resultInfo.setResult(false);resultInfo.setMsg("对不起,您无权访问");ObjectMapper mapper = new ObjectMapper();String resultJson = mapper.writeValueAsString(resultInfo);writer.write(resultJson);writer.flush();writer.close();}
}

11.2 未登录就访问的AuthencationEntryPoint

@Component
public class UrlAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {ResultInfo resultInfo = new ResultInfo();response.setCharacterEncoding("UTF-8");PrintWriter writer = response.getWriter();resultInfo.setResult(false);resultInfo.setMsg("未登录,请先登录");ObjectMapper mapper = new ObjectMapper();String resultJson = mapper.writeValueAsString(resultInfo);writer.write(resultJson);writer.flush();writer.close();}
}

11.3 注销成功的LogoutSuccessHandler

@Component
public class UrlLogoutSuccessHandler implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {ResultInfo resultInfo = new ResultInfo();response.setCharacterEncoding("UTF-8");response.setStatus(200);PrintWriter writer = response.getWriter();resultInfo.setResult(true);resultInfo.setMsg("注销成功");ObjectMapper mapper = new ObjectMapper();String resultJson = mapper.writeValueAsString(resultInfo);writer.write(resultJson);writer.flush();writer.close();}
}

引用借鉴https://blog.csdn.net/weixin_45452416/article/details/109528425


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部