Springboot引入springSecurity

1:添加Spring Security依赖

        <!-- Spring Security -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

2:添加完以后启动项目

2.1:此时控制台出现变化,出现一行密码

2.2:再次访问项目地址http://localhost:8081/

此时会出现权限框架的登录页

默认的用户名是: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类中再进行细化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值