导入springsecurity的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
就会有安全管理验证密码
账号:user 密码:刚刚生成的:密码字段
登录成功说明导入依赖成功!
二:去实体类实现UserDetails接口
并实现它的方法!
第三步编写controller以及service login登录实现方法
@Override
public Result login(LoginVo loginVo) {
log.info("1.开始登录");
UserDetails userDetails = userDetailsService.loadUserByUsername(loginVo.getUsername());
log.info("2.判断账号密码是正确");
if(userDetails == null || !passwordEncoder.matches(loginVo.getPassword(),userDetails.getPassword())){
return Result.fail("账号或密码错误,请重新输入!");
}
if(!userDetails.isEnabled()){
return Result.fail("该账号已经被禁用,请联系管理员!");
}
log.info("3.将该数据存入security容器中");
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null);
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
log.info("4.根据登录信息获取token");
String token="";
HashMap<String, String> map = new HashMap<>();
map.put("tokenHead",tokenHead);
map.put("token",token);
return Result.success("登录成功!",map);
}
这里的token需要后面jwt生成的token
以及yml配置jwt相应的信息
jwt:
#请求头
tokenHeader: Authorization
#签名加密
secret: 123456
#过期时间
expiration: 1800
#token头部
tokenHead: 'Bearer '
编写utils.jwt工具类
package com.intelligent.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* jwt生成token
*/
@Component
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME = "username";
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateJwt(claims);
}
/**
* 根据荷载生成token
* @param claims
* @return
*/
public String generateJwt(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret)
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.compact();
}
/**
* 从token中获取用户名
* @param token
* @return
*/
public String getUserNameFromToken(String token) {
Claims tokenBody = getTokenBody(token);
String username = (String) tokenBody.get(CLAIM_KEY_USERNAME);
return username;
}
/**
* token是否过期
* @param token
* @return
*/
public boolean isExpiration(String token) {
return getTokenBody(token).getExpiration().before(new Date());
}
/**
* 验证token是否有效
* @param token
* @param userDetails
* @return
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isExpiration(token);
}
/**
* 判断token是否可以被刷新
* @param token
* @return
*/
public boolean canRefresh(String token) {
return !isExpiration(token);
}
/**
* 刷新token
* @param token
* @return
*/
public String refreshToken(String token) {
Claims claims = getTokenBody(token);
claims.setExpiration(new Date());
return generateJwt(claims);
}
/**
* 获取token中的信息
* @param token
* @return
*/
public Claims getTokenBody(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
return null;
}
}
}
开始编写 Security相关的配置文件
package com.intelligent.security;
import com.intelligent.entity.SysUser;
import com.intelligent.handler.JwtAccessDeniedHandler;
import com.intelligent.handler.JwtAuthenticationEntryPoint;
import com.intelligent.handler.JwtAuthenticationFilter;
import com.intelligent.mapper.SysUserMapper;
import com.intelligent.security.content.SecurityContents;
import com.intelligent.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
//权限的基本配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAccessDeniedHandler jwtAccessDeniedHandler;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
//
@Override
public void configure(WebSecurity web) {
web.ignoring().mvcMatchers(SecurityContents.WHITE_LIST)
.antMatchers(SecurityContents.WHITE_LIST);
// web.ignoring()
// .antMatchers("/doc.html",
// "/webjars/**",
// "/img.icons/**",
// "/swagger-resources/**",
// "/v2/api-docs");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 1.使用jwt 关闭跨域攻击
http.csrf().disable();
//关闭session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//3.请求需要认证才能访问
http.authorizeRequests().anyRequest().authenticated();
//4.关闭缓存
http.headers().cacheControl();
//5.token过滤器,校验token
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
//6.没有登录,没有权限访问资源自定义
http.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).accessDeniedHandler(jwtAccessDeniedHandler);
}
//将自定义的登录 将它放入security中进行认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
以及需要放行的SecurityContents文件内容
package com.intelligent.security.content;
public class SecurityContents {
public static final String[] WHITE_LIST = {
//登录接口
"/user/login",
"/user/captcha",
"/user/captcha1",
"/user/test",
"/api/user/login",
//swagger
"/doc.html",
"/favicon.ico",
"/swagger-ui.html",
"/webjars/**",
"/swagger-resources/**",
"/v2/*",
"/configuration/ui",
"/configuration/security",
};
}
再重新实现UserDetails loadUserByUsername的方法
package com.intelligent.security.service;
import com.intelligent.entity.SysRole;
import com.intelligent.entity.SysUser;
import com.intelligent.mapper.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@Service
@Slf4j
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private SysUserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser user = userMapper.findByUsername(username);
if(user==null){
throw new UsernameNotFoundException("账号或者密码错误!");
}
if(user.getAdmin()){
ArrayList<SysRole> list = new ArrayList<>();
SysRole role = new SysRole();
role.setCode("admin");
list.add(role);
user.setRoles(list);
//user.setMenus();
}else {
user.setRoles(userMapper.findRoles(user.getId()));
user.setPermissions(userMapper.findPermissionById(user.getId()));
}
return user;
}
}
因为Security需要过滤前端传入的接口数据所以要配置三个配置类结构
jwt的三个配置类分别过滤权限,token,以及过期时间,登录功能
package com.intelligent.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.intelligent.utils.Result;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 查看是否有对应的权限和功能
*/
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setStatus(403);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter writer = response.getWriter();
writer.write(new ObjectMapper().writeValueAsString(Result.fail("权限不足,请联系管理员")));
writer.flush();
writer.close();
}
}
package com.intelligent.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.intelligent.utils.Result;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
//当用户未登录和token过期 时访问资源
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter writer = response.getWriter();
writer.write(new ObjectMapper().writeValueAsString(Result.fail("您尚未登录,请登录")));
writer.flush();
writer.close();
}
}
package com.intelligent.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.intelligent.utils.Result;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
//当用户未登录和token过期 时访问资源
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter writer = response.getWriter();
writer.write(new ObjectMapper().writeValueAsString(Result.fail("您尚未登录,请登录")));
writer.flush();
writer.close();
}
}
三个类分别过滤以及再上述的SecurityConfig中配置
上述配置好后记得修改SysUserServiceImpl 里面token ,将“” 改成用token工具类生产的工具
前端访问获取到token信息 获取成功!以后每个资源访问都必须带上这个头部信息