springboot整合springsecurity进行token登录认证

之前写过一篇的,但是感觉有点垃圾,这次重新重写了一遍。细节不说了.
首先看一下请求方法

@Autowired
LoginService loginService;

@PostMapping("/login")
public Result login(@RequestBody LoginUser loginUser){
	String token = loginService.login(loginUser);
	return Result.ok(token,"登录成功");
}

@GetMapping("/t1")
@PreAuthorize("hasAuthority('sys:user:edit1')")
public Result t1(){
	return Result.ok("没有权限的");
}

@GetMapping("/t2")
@PreAuthorize("hasAuthority('sys:user:del')")
public Result t2(){
	Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
	Object principal = authentication.getPrincipal();
	return Result.ok(principal,"有权限的2");
}

@GetMapping("/noPer")
public String noPer(){
	Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
	System.err.println(authentication);
	return "不需要权限的";
}

效果图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

下面直接上代码
登录实体类

@Data
public class LoginUser implements Serializable{

	private static final long serialVersionUID = 1L;

	private String username;//用户名
	
	private String password;//密码
	
    private String token;//登录认证

    private Long loginTime;//登录时间

    private Long expireTime;//过期时间
    
    //private SysUserEntity sysUserEntity;
    private List<String> permsList;//权限集合
}

登录方法

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
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.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import nyist.com.project.entity.LoginUser;
import nyist.com.project.utils.RedisUtil;

@Component
public class LoginService {

	@Autowired
	RedisUtil redisUtil;
	
	@Resource
    private AuthenticationManager authenticationManager;
	
	public String login(LoginUser loginUser) {
		String username = loginUser.getUsername();
		try {
			Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, loginUser.getPassword()));//执行认证方法
		} catch (Exception e) {
			throw new BadCredentialsException("用户认证错误");
		}
		//这里模拟查询出权限,放在这里主要是为了避免在userdetailService登录认证错误的时候就把数据权限查出来了,所以放在这里
		String[] perms = {"sys:user:del","sys:user:add","sys:user:update"};
		List<String> permsList= new ArrayList<>();
		for(String perm: perms) {
			permsList.add(perm);
		}
        loginUser.setPermsList(permsList);
        String uuid = UUID.randomUUID().toString().replace("-", "");
		loginUser.setToken(uuid);
		long currentTimeMillis = System.currentTimeMillis();
		long e= 30 * 60 * 1000L;//三十分钟
		long expireTime = currentTimeMillis+e;
		loginUser.setExpireTime(expireTime);
		redisUtil.set(uuid, loginUser, 60*30);//redis三十分钟有效期
		return uuid;
	}
}

自定义的UserDetailsService

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 org.springframework.util.StringUtils;

import nyist.com.project.entity.LoginUser;
import nyist.com.project.utils.MD5Util;

/**
 * 用户验证
 * @author ljw
 *
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService{

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
    	if(StringUtils.isEmpty(username)||!username.equals("ljw")) {
    		throw new UsernameNotFoundException("用户:" + username + " 不存在");
    	}
    	//模拟从数据库查出来用户
    	LoginUser loginUser = new LoginUser();
    	loginUser.setUsername(username);
    	String pwd = "ljw";
		String lower = MD5Util.md5Encrypt32Lower(pwd);
		//由于设置了密码加密方式,这里查出来的用户密码肯定是加密过的,所以在这里手动处理一下,不然一会认证会失败
        return new User(username, lower, new ArrayList<GrantedAuthority>());
    }
}

核心配置类

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.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.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import nyist.com.project.utils.MD5Util;

/**
 *	核心配置类 
 * @author ljw
 *
 */
@Component
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	@Autowired
	UserDetailsService userDetailsService;
	
	@Autowired
	TokenFilter tokenFilter;
	
	/**
	 * 指定资源放行
	 */
	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers(
				"/static/**",
				"/test/**",
				"/login",
				"/noPer",
				"/logout",
				"/doc.html",
				"/webjars/**",
				"/swagger-resources/**",
				"/v2/api-docs/**");
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
			@Override
			public String encode(CharSequence rawPassword) {
				String password = rawPassword+"";
				return DigestUtils.md5DigestAsHex(password.getBytes());
			}
			@Override
			public boolean matches(CharSequence rawPassword, String encodedPassword) {
				String password = rawPassword+"";
				return MD5Util.md5Encrypt32Lower(password).equals(encodedPassword);
			}
		});
	}
	
	@Override
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 验证码captchaImage 允许匿名访问
                .antMatchers("/login", "/captchaImage").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/",
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/profile/**"
                ).permitAll()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/common/download/resource**").anonymous()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                //处理跨域
                .and()
                .cors()
                .and()
                .csrf().disable();
        //httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
        httpSecurity.exceptionHandling().authenticationEntryPoint(new UnauthorizedHandler());//匿名用户访问无权限资源时的异常
    }

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

操作拦截过滤器

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeanUtils;
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 nyist.com.project.entity.LoginUser;
import nyist.com.project.utils.RedisUtil;

/**
 * token过滤验证器
 * @author ljw
 *
 */
@Component
public class TokenFilter extends OncePerRequestFilter{


	@Autowired
	RedisUtil redisUtil;
	
	@Autowired
	HttpServletResponse response;
	
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException{
    	LoginUser loginUser = getLoginUser(request);
        if (loginUser!=null ){
       	    //这一块解决 GrantedAuthority 等参数无法序列化,懒得去找解决方案,所以也就没去自定义实现UserDetails
        	List<String> permsList = loginUser.getPermsList();
        	List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();  
        	permsList.forEach(each->{
        		authorities.add(new SimpleGrantedAuthority(each));
        	});
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthList());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        chain.doFilter(request, response);
    }

	private LoginUser getLoginUser(HttpServletRequest request) throws IOException {
		String token = request.getHeader("Authorization");
		if(token==null) {
			return null;
		}
		Object object = redisUtil.get(token);
		if(object==null) {
			return null;
		}
		LoginUser loginUser = (LoginUser)redisUtil.get(token);
		long currentTimeMillis = System.currentTimeMillis();
		long e= 30 * 60 * 1000L;//三十分钟
		if(currentTimeMillis>=loginUser.getExpireTime()) {
			return null;
		}
		long refresTime=  loginUser.getExpireTime()-System.currentTimeMillis();
		if(refresTime<=5 * 60 * 1000L) {//有效期小于等于五分钟,刷新token
			long expireTime = currentTimeMillis+e;
			loginUser.setExpireTime(expireTime);
		}
		redisUtil.set(token, loginUser, 60*30);//redis token再次刷新值
		return loginUser;
	}
}

认证失败提示处理

/**
 * 未登录认证处理
 * @author ljw
 *
 */
public class UnauthorizedHandler implements AuthenticationEntryPoint{

	@Override
	  public void commence(HttpServletRequest request, HttpServletResponse response,
	             AuthenticationException authException) throws IOException, ServletException {
	    response.setCharacterEncoding("utf-8");
	    response.setContentType("text/javascript;charset=utf-8");
	    response.getWriter().print(JSONObject.toJSONString(Result.fail("您未登录,请先登录~")));
	  }
}

全局异常处理

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException.Unauthorized;

import nyist.com.project.result.Result;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 定义全局运行异常
 * @author ljw 2020年11月2日14:36:07
 *
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
	
	private  final  static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler. class);
	/**
	 * 未登录
	 * @param req
	 * @return
	 */
	@ExceptionHandler(Unauthorized.class)
	public Result NoPerm(Exception e){
		return Result.fail("请您先登录"+e.getMessage());
	}
	
	/**
	 * 无权限
	 * @param e
	 * @return
	 */
	@ExceptionHandler(AccessDeniedException.class)
	public Result accessDeniedException(Exception e){
		return Result.fail("暂无权限:"+e.getMessage());
	}
	
	
	/**
	 * 全局异常处理
	 * @param e
	 * @param req
	 * @return
	 */
	@ExceptionHandler(value= RuntimeException.class)
	public Result runTimeException(RuntimeException e,HttpServletRequest req){
		logger.error("错误请求地址URI [{}]", req.getRequestURI());
		return Result.fail(e.getMessage());
	}
}

RedisUtil工具类网上一大堆,最后配置一下序列化方式,方便自己查看
至于jwt什么的,可在此基础上扩展,用法差不多

好的,你想了解如何在Spring Boot中整合Spring Security和OAuth 2.0来实现token认证。下面是一个基本的步骤: 1. 添加依赖:在你的`pom.xml`文件中添加Spring Security和OAuth 2.0的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> ``` 2. 配置Spring Security:创建一个继承自`WebSecurityConfigurerAdapter`的配置类,并覆盖`configure`方法,配置Spring Security。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/oauth2/**", "/login/**", "/logout/**") .permitAll() .anyRequest() .authenticated() .and() .oauth2Login() .loginPage("/login") .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/") .deleteCookies("JSESSIONID"); } } ``` 这个配置类指定了哪些URL需要进行认证,`antMatchers`方法指定了不需要认证的URL。 3. 配置OAuth 2.0客户端:创建一个继承自`WebSecurityConfigurerAdapter`的配置类,并使用`@EnableOAuth2Client`注解开启OAuth 2.0客户端。 ```java @Configuration @EnableOAuth2Client public class OAuth2ClientConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 配置HTTP Security } @Bean public OAuth2AuthorizedClientService authorizedClientService( OAuth2ClientRegistrationRepository clientRegistrationRepository) { return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository); } } ``` 这个配置类可以用来配置OAuth 2.0的客户端,你可以在`configure`方法中添加一些额外的配置。 4. 配置OAuth 2.0客户端注册:在`application.properties`文件中配置OAuth 2.0的客户端注册信息。 ```properties spring.security.oauth2.client.registration.my-client-id.client-id=your-client-id spring.security.oauth2.client.registration.my-client-id.client-secret=your-client-secret spring.security.oauth2.client.registration.my-client-id.scope=your-scopes spring.security.oauth2.client.registration.my-client-id.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.my-client-id.redirect-uri=your-redirect-uri ``` 这个配置文件中的`my-client-id`是你自己指定的客户端ID,你需要将其替换为你自己的信息。 这些步骤是实现Spring Boot中整合Spring Security和OAuth 2.0实现token认证的基本步骤。你可以根据自己的需求进行进一步的配置和扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值