1、添加相关依赖
主要是springsecurity和jwt依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.njwj</groupId>
<artifactId>gx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gx</name>
<description>guanxian</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- json转换依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<!--springsecurity依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--jwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 通用mapper的依赖-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.4</version>
</plugin>
</plugins>
</build>
</project>
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<User> {
}
3.3 UserService
public interface UserService {
Map<String, String> addUser(User user);
User findByUserName(String username);
List<User> findAll();
Map<String,String> deleteUser(Integer id);
}
3.4 UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public Map<String, String> addUser(User user) {
Map<String, String> 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;
}
@Override
public User findByUserName(String username) {
User user = new User();
user.setUserName(username);
return userMapper.selectOne(user);
}
@Override
public List<User> findAll() {
return userMapper.selectAll();
}
@Override
public Map<String, String> deleteUser(Integer id) {
Map<String, String> 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<String, Object> 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<? extends GrantedAuthority> 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<? extends GrantedAuthority> 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 {
@Autowired
private UserService userService;
@Override
public 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");
}
@Override
public 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并返回
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws IOException, ServletException {
JWTUser jwtUser = (JWTUser) authResult.getPrincipal();
String role = "";
Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities();
for (GrantedAuthority authority : authorities) {
role = authority.getAuthority();
}
//生成token
String 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();
}
// 失败 返回错误就行
@Override
protected 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
@Override
protected 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 {
@Autowired
private JWTUserService jwtUserService;
@Autowired
private UrlLogoutSuccessHandler logoutSuccessHandler;
@Autowired
private UrlAccessDeniedHandler accessDeniedHandler;
@Autowired
private UrlAuthenticationEntryPoint authenticationEntryPoint;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 这边 通过重写configure(),去数据库查询用户是否存在
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserService).passwordEncoder(bCryptPasswordEncoder());
}
@Override
protected 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);
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
10、UserController
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@PostMapping(value = "/addUser")
public @ResponseBody ResultInfo addUser(@RequestBody User user){
user.setUserPassword(bCryptPasswordEncoder.encode(user.getUserPassword()));
Map<String,String> resultMap = userService.addUser(user);
ResultInfo<DownloadAreaRecordData> 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;
}
@GetMapping
public User findByUserName(String userName){
return userService.findByUserName(userName);
}
@GetMapping("/findAll")
@ResponseBody
@PreAuthorize("hasAnyAuthority('admin')")
public ResultInfo findAll(){
ResultInfo<User> resultInfo = new ResultInfo<>();
List<User> 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<String,String> resultMap = userService.deleteUser(id);
ResultInfo<DownloadAreaRecordData> 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 {
@Override
public 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 {
@Override
public 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 {
@Override
public 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