1:添加Spring Security依赖
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2:添加完以后启动项目
2.1:此时控制台出现变化,出现一行密码
此时会出现权限框架的登录页
默认的用户名是:user,密码:控制台上出现的密码
输入以后才能访问接口信息
3:编写SpringSecurity核心配置类,实现自定义登录
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserDetailsServiceImpl UserDetailsService;
@Resource
private JwtAuthenticationEntryPoint authenticationEntryPoint;
@Resource
private JwtAccessDeniedHandler accessDeniedHandler;
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private JwtAuthenticationFilter authenticationFilter;
/**
* 配置白名单
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().mvcMatchers(SpringSecurityConstant.NONE_SECURITY_URL_PATTERNS);
}
/**
* Security的核心配置
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 1.使用jwt,关闭csrf
http.csrf().disable();
// 2.基于token认证,关闭session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 3.配置白名单 除白名单以外的都进行认证
http.authorizeRequests().anyRequest().authenticated();
// 4.禁用缓存
http.headers().cacheControl();
// 5.添加jwt登录授权的过滤器
http.addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class);
// 6.添加未授权和未登录的返回结果
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint);
}
/**
* 登录认证
* 自定义登录逻辑的配置
* 也就是配置的security进行认证
* */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(UserDetailsService).passwordEncoder(passwordEncoder);
}
}
3.1:token认证过滤器,在接口访问前进行过滤
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Resource
private TokenUtil tokenUtil;
@Resource
private UserDetailsService userDetailsService;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
//1:获取请求头中的token
String token = request.getHeader(tokenHeader);
//2:判断token是否存在
if(!StrUtil.isBlank(token)){
//根据token获取用户名
String userName = tokenUtil.getUserNameFromToken(token);
//3:token存在但是security里面没有登录信息,代表有token但是没登录
if(null == SecurityContextHolder.getContext().getAuthentication()){
//没有登录信息,直接登录
UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
//判断token是否有效,token没有过期,并且和userdetail中的username一样,那么就将security中的登录信息进行刷新
if(tokenUtil.isExpiration(token) && userName.equals(userDetails.getUsername())){
//刷新security中的用户信息
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
chain.doFilter(request, response);
}
}
3.2:重写WebSecurityConfigurerAdapter下的userDetailsService 实现自定义的登录
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Resource
private SysUserService sysUserService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserService.findByUserName(username);
if(null != sysUser){
return sysUser;
}
throw new RuntimeException("用户名或密码错误");
}
}
3.3:用户访问无权限资源时候的异常
@Configuration
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.setStatus(401);
PrintWriter printWriter = response.getWriter();
printWriter.write(new ObjectMapper().writeValueAsString(Result.fail("您尚未登录或登录信息已过期,请重新登录!")));
printWriter.flush();
printWriter.close();
}
}
3.4:当用户权限不足情况下访问资源的返回异常
@Configuration
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.setStatus(403);
PrintWriter printWriter = response.getWriter();
printWriter.write(new ObjectMapper().writeValueAsString(Result.fail("权限不足,请联系管理员!")));
printWriter.flush();
printWriter.close();
}
}
3.5:白名单配置类
public class SpringSecurityConstant {
/**
* 放开权限校验的接口
*/
public static final String[] NONE_SECURITY_URL_PATTERNS = {
//前端的
"/favicon.ico",
//swagger相关的
"/doc.html",
"/webjars/**",
"/swagger-resources/**",
"/v2/api-docs",
"/v2/api-docs-ext",
"/configuration/ui",
"/configuration/security",
//后端的
"/login",
"/sysLogin"
};
}
3.6:编写Bean配置
@Configuration
public class BeanConfig {
@Bean
public JwtAuthenticationFilter authenticationFilter() {
return new JwtAuthenticationFilter();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4:编写跨域配置类,前后端分离项目
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
//允许访问路径
.addMapping("/**")
//配置请求来源
.allowedOrigins("http://localhost:8081")
//配置允许访问的方法
.allowedMethods("GET","POST","PUT","DELETE","OPTION")
//配置最大响应时间 6秒
.maxAge(3600)
//是否允许携带参数
.allowCredentials(true)
//允许请求头
.allowedHeaders();
}
}
5:进行登录编写,具体实现逻辑
5.1:编写用户登录的实体类,实现UserDetails
@Data
public class SysUser implements UserDetails {
private Long id;
private String userName;
private String passWord;
private Integer age;
private Character sex;
private String avatar;
private String address;
private String openId;
private Boolean status;
private Boolean admin;
private String phoneNumber;
/**
* 权限数据
* */
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
/**
* 获取用户名
* */
@Override
public String getUsername() {
return null;
}
/**
* 账号是否过期
* */
@Override
public boolean isAccountNonExpired() {
return false;
}
/**
* 账号是否被锁定
* */
@Override
public boolean isAccountNonLocked() {
return false;
}
/**
* 凭证是否过期
* */
@Override
public boolean isCredentialsNonExpired() {
return false;
}
/**
* 是否被禁用
* */
@Override
public boolean isEnabled() {
//直接返回数据库中查出的用户状态
return status;
}
}
?5.2:定义Controller类,添加登录方法
@RestController
@Api("用户信息登录控制器")
public class LoginConyroller {
@Autowired
private UserService userService;
@Autowired
private RedisUtil redisUtil;
@PostMapping("/sysLogin")
@ApiOperation(value = "系统用户登录", httpMethod = "POST")
public Result sysLogin(@RequestBody @Valid SysLoginVo sysLoginVo) {
return userService.sysLogin(sysLoginVo);
}
@GetMapping("/logOut")
@ApiOperation(value = "退出登录", httpMethod = "GET")
public Result logOut(HttpServletRequest request, HttpServletResponse
response,Principal principal ) {
//清除spring security用户认证信息
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//清除redis中的token信息
redisUtil.delKey(principal.getName());
if(null != auth) {
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return new Result(MessageConstant.SUCCESS_CODE, MessageConstant.LOGOUT_SUCCESS);
}
}
?5.3:定义userService接口
public interface UserService {
/**
* 登录
* @param loginVo
* @return
*/
Result sysLogin(SysLoginVo sysLoginVo);
}
?5.5:实现userService接口
@Service
public class SysUserServiceImpl implements SysUserService {
@Resource
private UserMapper userMapper;
@Resource
private UserDetailsService userDetailsService;
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private TokenUtil tokenUtil;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Override
public Result login(LoginVo loginVo) {
UserDetails userDetails = userDetailsService.loadUserByUsername(loginVo.getUserName());
//如果为空,则账号不存在,判断传过来的密码和userDetails里面的密码是否匹配
if(null == userDetails || !passwordEncoder.matches(loginVo.getPassWord(), userDetails.getPassword())){
return Result.fail("账号或密码错误,请重新输入");
}
//判断账号是否禁用
if(!userDetails.isEnabled()){
return Result.fail("该账号已禁用,请联系管理员");
}
//如果这些都验证通过,则需要在spring security中存入当前用户登录信息
//第一个参数是登录信息,密码,权限信息
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
//将权限信息设置进去
SecurityContextHolder.getContext()
.setAuthentication(usernamePasswordAuthenticationToken);
//根据登录信息,借助JWT获取token
String token = tokenUtil.generateToken(userDetails);
Map<String,String> map = new HashMap<>(2);
map.put("tokenHeader",tokenHeader);
map.put("token",token);
return Result.success("登录成功",map);
}
/**
* 根据用户名获取用户对象
* */
@Override
public SysUser findByUserName(String userName) {
return userMapper.findByUserName(userName);
}
}
至此,springboot集成springsecurity的配置demo完成,具体里面的权限配置还需要自己implements UserDetails类中再进行细化
|