SpringBoot集成Spring Security实现登陆和简单权限验证

   日期:2020-09-23     浏览:84    评论:0    
核心提示:1.数据库配置好2.导依赖 <!-- spring security 安全认证 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependen

1.数据库配置好

2.导依赖

        <!-- spring security 安全认证 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!--Token生成与解析-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jwt.version}</version>
        </dependency>

3.新建UserDetailsServiceImpl用户认证逻辑类实现UserDetailsService接口:

package com.xr.blog.tools.security;

import com.xr.blog.exception.CustomException;
import com.xr.blog.pojo.SysUser;
import com.xr.blog.service.SysPermissionService;
import com.xr.blog.service.SysUserService;
import com.xr.blog.tools.text.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;


@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);

    @Autowired
    private SysUserService userService;

    @Autowired
    private SysPermissionService permissionService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        //此处通过用户输入得用户名去数据库查找用户
        SysUser user = userService.findByUserName(username);
        //账号验证
        if (StringUtils.isNull(user)) {
            log.info("登录用户:{} 不存在.", username);
            throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
        }
        else if (UserStatus.DISABLE.getCode().equals(user.getIsDisable())) {
            log.info("登录用户:{} 已被禁用.", username);
            throw new CustomException("对不起,您的账号:" + username + " 已禁用");
        }
        return createLoginUser(user);
    }


    //创建登录信息,其中loginuser实现了UserDetails接口
    public UserDetails createLoginUser(SysUser user) {
        return new LoginUser(user, permissionService.getMenuPermission(user.getId()));
    }
}

4.新建SecurityConfig类继承WebSecurityConfigurerAdapter:

package com.xr.blog.tools.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    
    @Autowired
    private UserDetailsService userDetailsService;

    
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;

    
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception
    {
        return super.authenticationManagerBean();
    }

    
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .cors()
                .and()
                // CRSF禁用,因为不使用session
                .csrf().disable()
                // 认证失败处理类(未登录直接请求资源返回未认证)
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 验证码captchaImage 允许匿名访问
                .antMatchers("login", "captchaImage","/").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "*.html",
                        "*.css",
                        "*.js",
                        "logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }


    
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }

    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {

   //设置自定义认证类,并设置密码加密规则      
  auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

5.认证失败处理类AuthenticationEntryPointImpl实现AuthenticationEntryPoint,此类中得逻辑就是处理失败后返回什么信息给客户端,自定义即可:

package com.xr.blog.tools.security;

import com.alibaba.fastjson.JSON;
import com.xr.blog.tools.Result;
import com.xr.blog.tools.ServletUtils;
import com.xr.blog.tools.constant.Code;
import com.xr.blog.tools.text.StringUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;


@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable {
    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
            throws IOException {
        String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        ServletUtils.renderString(response, JSON.toJSONString(Result.of(Code.SC_UNAUTHORIZED.getState(), msg,msg)));
    }
}

6.退出处理类LogoutSuccessHandlerImpl实现LogoutSuccessHandler,此类用于用户退出操作时实现得逻辑:

package com.xr.blog.tools.security;

import com.alibaba.fastjson.JSON;
import com.xr.blog.tools.Result;
import com.xr.blog.tools.ServletUtils;
import com.xr.blog.tools.constant.Code;
import com.xr.blog.tools.text.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
    @Autowired
    private TokenService tokenService;

    
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser))
        {
            //String userName = loginUser.getUsername();
            // 删除用户缓存记录
            tokenService.delLoginUser(loginUser.getToken());
            // 记录用户退出日志
            //AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Code.LOGOUT, "退出成功"));
        }
        ServletUtils.renderString(response, JSON.toJSONString(Result.of(Code.SC_OK.getState(),"退出成功", "退出成功")));
    }
}

7.token认证过滤器JwtAuthenticationTokenFilter继承OncePerRequestFilter:

package com.xr.blog.tools.security;

import com.xr.blog.tools.text.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException
    {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
        {
            tokenService.verifyToken(loginUser);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        chain.doFilter(request, response);
    }
}

8.写一个controller层得登录接口:

package com.xr.blog.controller;

import com.xr.blog.service.LoginService;
import com.xr.blog.service.SysUserService;
import com.xr.blog.tools.Result;
import com.xr.blog.tools.constant.Code;
import com.xr.blog.tools.security.LoginBody;
import org.apache.commons.codec.DecoderException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.List;

@RestController
@RequestMapping("/squirrelblog")
public class LoginController {
    @Autowired
    LoginService loginService;
    @Autowired
    SysUserService sysUserService;

    @PostMapping(value = "/login",produces = "application/json;charset=UTF-8")
    public Result<String> login(@RequestBody LoginBody loginBody, HttpServletRequest request) throws NoSuchAlgorithmException, InvalidKeySpecException, DecoderException {
        return loginService.login(loginBody);
    }
}

9.LoginService 的login方法如下:

package com.xr.blog.service.impl;

import com.alibaba.fastjson.JSON;
import com.xr.blog.exception.CustomException;
import com.xr.blog.exception.UserPasswordNotMatchException;
import com.xr.blog.service.LoginService;
import com.xr.blog.tools.IdUtils;
import com.xr.blog.tools.Result;
import com.xr.blog.tools.constant.Code;
import com.xr.blog.tools.redis.RedisCache;
import com.xr.blog.tools.security.LoginBody;
import com.xr.blog.tools.security.LoginUser;
import com.xr.blog.tools.security.TokenService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.codec.DecoderException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;

@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private TokenService tokenService;
    @Autowired
    RedisCache redisCache;
    @Resource
    private AuthenticationManager authenticationManager;
    // 令牌秘钥
    @Value("${token.secret}")
    private String secret;

    @Override
    public Result<String> login(LoginBody loginBody) throws NoSuchAlgorithmException, DecoderException, InvalidKeySpecException {
        //校验验证码
        String verifyKey = Code.CAPTCHA_CODE_KEY + loginBody.getUuid();
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null){
            return Result.of(Code.VAILDATE_ERROR.getState(),"验证码已过期","验证码已过期");
        }
        if(!captcha.equalsIgnoreCase(loginBody.getCode())){
            return Result.of(Code.VAILDATE_ERROR.getState(),"验证码错误","验证码错误");
        }

        // 用户验证
        Authentication authentication = null;
        try
        {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginBody.getUsername(), loginBody.getPassword()));
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                throw new UserPasswordNotMatchException();
            }
            else
            {
                throw new CustomException(e.getMessage());
            }
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        return Result.of(Code.SC_OK.getState(),Code.SC_OK.getDescription(),tokenService.createToken(loginUser));
    }
}

10.接下来测试登录,未登录请求后台接口就好了。。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服