SpringBoot整合SpringSecurityOauth2实现鉴权()

  本篇文章为你整理了SpringBoot整合SpringSecurityOauth2实现鉴权()的详细内容,包含有 SpringBoot整合SpringSecurityOauth2实现鉴权,希望能帮助你了解 SpringBoot整合SpringSecurityOauth2实现鉴权。

  思考:为什么需要鉴权呢?

  系统开发好上线后,API接口会暴露在互联网上会存在一定的安全风险,例如:爬虫、恶意访问等。因此,我们需要对非开放API接口进行用户鉴权,鉴权通过之后再允许调用。

  

  准备

  spring-boot:2.1.4.RELEASE

  spring-security-oauth2:2.3.3.RELEASE(如果要使用源码,不要随意改动这个版本号,因为2.4往上的写法不一样了)

  mysql:5.7

  

  效果展示

  这边只用了postman做测试,暂时未使用前端页面来对接,下个版本角色菜单权限分配的会有页面的展示

  

  1、访问开放接口http://localhost:7000/open/hello

  

  

  2、不带token访问受保护接口http://localhost:7000/admin/user/info

  

  

  3、登录后获取token,带上token访问,成功返回了当前的登录用户信息

  

  

  

  

  实现

  oauth2一共有四种模式,这边就不做讲解了,网上搜一搜,千篇一律

  因为现在只考虑做单方应用的,所以使用的是密码模式。

  后面会出一篇SpringCloud+Oauth2的文章,网关鉴权

  

  讲一下几个点吧

  1、拦截器配置动态权限

  

  新建一个 MySecurityFilter类,继承AbstractSecurityInterceptor,并实现Filter接口

  初始化,自定义访问决策管理器

  

@PostConstruct

 

   public void init(){

   super.setAuthenticationManager(authenticationManager);

   super.setAccessDecisionManager(myAccessDecisionManager);

   }

 

  

  自定义 过滤器调用安全元数据源

  

@Override

 

  public SecurityMetadataSource obtainSecurityMetadataSource() {

   return this.mySecurityMetadataSource;

  }

 

  

  

  先来看一下自定义过滤器调用安全元数据源的核心代码

  以下代码是用来获取到当前请求进来所需要的权限(角色)

  

  

/**

 

   * 获得当前请求所需要的角色

   * @param object

   * @return

   * @throws IllegalArgumentException

   @Override

   public Collection ConfigAttribute getAttributes(Object object) throws IllegalArgumentException {

   String requestUrl = ((FilterInvocation) object).getRequestUrl();

   if (IS_CHANGE_SECURITY) {

   loadResourceDefine();

   if (requestUrl.indexOf("?") -1) {

   requestUrl = requestUrl.substring(0, requestUrl.indexOf("?"));

   UrlPathMatcher matcher = new UrlPathMatcher();

   List Object list = new ArrayList (); //无需权限的,直接返回

   list.add("/oauth/**");

   list.add("/open/**");

   if(matcher.pathsMatchesUrl(list,requestUrl))

   return null;

   Set String roleNames = new HashSet();

   for (Resc resc: resources) {

   String rescUrl = resc.getResc_url();

   if (matcher.pathMatchesUrl(rescUrl, requestUrl)) {

   if(resc.getParent_resc_id() != null resc.getParent_resc_id().intValue() == 1){ //默认权限的则只要登录了,无需权限匹配都可访问

   roleNames = new HashSet();

   break;

   Map map = new HashMap();

   map.put("resc_id", resc.getResc_id());

   // 获取能访问该资源的所有权限(角色)

   List RoleRescDTO roles = roleRescMapper.findAll(map);

   for (RoleRescDTO rr : roles)

   roleNames.add(rr.getRole_name());

   Set ConfigAttribute configAttributes = new HashSet();

   for(String roleName:roleNames)

   configAttributes.add(new SecurityConfig(roleName));

   log.debug("【所需的权限(角色)】:" + configAttributes);

   return configAttributes;

   }

 

  

  再来看一下自定义访问决策管理器核心代码,这段代码主要是判断当前登录用户(当前登录用户所拥有的角色会在最后一项写到)是否拥有该权限角色

  

@Override

 

   public void decide(Authentication authentication, Object o, Collection ConfigAttribute configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

   if(configAttributes == null){ //属于白名单的,不需要权限

   return;

   Iterator ConfigAttribute iterator = configAttributes.iterator();

   while (iterator.hasNext()){

   ConfigAttribute configAttribute = iterator.next();

   String needPermission = configAttribute.getAttribute();

   for (GrantedAuthority ga: authentication.getAuthorities()) {

   if(needPermission.equals(ga.getAuthority())){ //有权限,可访问

   return;

   throw new AccessDeniedException("没有权限访问");

   }

 

  

  2、自定义鉴权异常返回通用结果

  为什么需要这个呢,如果不配置这个,对于前端,后端来说都很难去理解鉴权失败返回的内容,还不能统一解读,废话不多说,先看看不配置和配置了的返回情况

  (1)未自定义前,没有携带token去访问受保护的API接口时,返回的结果是这样的

  

  (2)我们规定一下,鉴权失败的接口返回接口之后,变成下面这种了,是不是更利于我们处理和提示用户

  

  

  

  好了,来看一下是在哪里去配置的吧

  我们资源服务器OautyResourceConfig,重写下下面这部分的代码,来自定义鉴权异常返回的结果

  大伙可以参考下这个https://blog.csdn.net/Pastxu/article/details/124538364

  

@Override

 

   public void configure(ResourceServerSecurityConfigurer resources) throws Exception {

   resources.authenticationEntryPoint(authenticationEntryPoint) //token失效或没携带token时

   .accessDeniedHandler(requestAccessDeniedHandler); //权限不足时

   }

 

  

  

  3、获取当前登录用户

  第一种:使用JWT携带用户信息,拿到token后再解析

  暂不做解释

  第二种:写一个SecurityUser实现UserDetails接口(这个工程中使用的是这一种)

  原来的只有UserDetails接口只有username和password,这里我们加上我们系统中的User

  

protected User user;

 

   public SecurityUser(User user) {

   this.user = user;

   public User getUser() {

   return user;

   }

 

  

  在BaseController,每个Controller都会继承这个的,在里面写给getUser()的方法,只要用户带了token来访问,我们可以直接获取当前登录用户的信息了

  

protected User getUser() {

 

   try {

   SecurityUser userDetails = (SecurityUser) SecurityContextHolder.getContext().getAuthentication()

   .getPrincipal();

   User user = userDetails.getUser();

   log.debug("【用户:】:" + user);

   return user;

   } catch (Exception e) {

   return null;

   }

 

  

  那么用户登录成功后,如何去拿到用户的角色集合等呢,这里面就要实现UserDetailsService接口了

  

  

@Service

 

  public class TokenUserDetailsService implements UserDetailsService{

   @Autowired

   private LoginService loginService;

   @Override

   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

   User user = loginService.loadUserByUsername(username); //这个我们拎出来处理

   if(Objects.isNull(user))

   throw new UsernameNotFoundException("用户名不存在");

   return new SecurityUser(user);

  }

 

  

  然后在我们的安全配置类中设置UserDetailsService为上面的我们自己写的就行

  

@Override

 

   protected void configure(AuthenticationManagerBuilder auth) throws Exception {

   auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());

   }

 

  

  最后我们只需要在loginService里面实现我们的方法就好,根据我们的实际业务处理判断该用户是否存在等

  

@Override

 

   public User loadUserByUsername(String username){

   log.debug(username);

   Map map = new HashMap();

   map.put("username",username);

   map.put("is_deleted",-1);

   User user = userMapper.findByUsername(map);

   if(user != null){

   map = new HashMap();

   map.put("user_id",user.getUser_id());

   //查询用户的角色

   List UserRoleDTO userRoles = userRoleMapper.findAll(map);

   user.setRoles(listRoles(userRoles));

   //权限集合

   Collection ? extends GrantedAuthority authorities = merge(userRoles);

   user.setAuthorities(authorities);

   return user;

   return null;

   }

 

  

  

  大功告成啦,赶紧动起手来吧!

  附上源码地址:https://gitee.com/jae_1995/spring-boot-oauth2

  数据库文件在这

  

  

  

  

  点个小赞呗

  

  以上就是SpringBoot整合SpringSecurityOauth2实现鉴权()的详细内容,想要了解更多 SpringBoot整合SpringSecurityOauth2实现鉴权的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: