一、SpringSecurity + oauth2的配置与使用

一、SpringSecurity

1、springsecurity的配置

1SecurityConfig
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private TokenManager tokenManager;

    @Autowired
    private RedisTemplate redisTemplate;

//    配置数据源 操作数据库
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(false); //不创建新表
        return jdbcTokenRepository;
    }



//    BCryptPasswordEncoder使用散列函数SHA-256 +随机盐+密钥对密码进行加密
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

//    permitAll()无条件访问


    /**
     * 设置记住我
     * <http>
     * ...
     * <remember-me data-source-ref="someDataSource"/>
     * </http>
     *
     * 框架在认证的时候请求的配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
//        配置退出页面
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/logout/index").permitAll();
//        没有权限则跳到uauth.html
        http.exceptionHandling().accessDeniedPage("uauth.html");
        http.formLogin()
                .loginPage("/login.html") //登录的页面
                .loginProcessingUrl("/user/login") //登录访问路径
                .defaultSuccessUrl("/index").permitAll()//登录成功跳转页面
                .and().authorizeRequests()
                    .antMatchers("/","/user/login").permitAll() //那些路径可以直接访问

                .anyRequest().authenticated() //任何请求都必须经过身份验证,否则我的Spring应用程序将返回401响应 .
//                设置记住我(自动登录,有效时长60s)
                .and()
                .rememberMe()
                .rememberMeParameter("rememberMe")
                .rememberMeCookieName("remember-me-cookie")
                .tokenRepository(persistentTokenRepository())  //写入数据库
                .tokenValiditySeconds(60*60*24*7)
                .userDetailsService(userDetailsService)
                .and()

                .logout().logoutUrl("/admin/acl/index/logout")//退出路径
                .addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate))
                .and()
                .addFilterBefore(new PreFilter() , TokenLoginFilter.class)
                .addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
                .addFilter(new TokenAuthFilter(authenticationManager(), tokenManager, redisTemplate))
               .and().logout().logoutUrl("/admin/acl/index/logout")//退出路径
                .addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and()
                .addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
                .addFilter(new TokenAuthFilter(authenticationManager(), tokenManager, redisTemplate))
                .httpBasic();

    }

    /**
     * 在认证的时候使用哪个登录逻辑
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}

   //不进行认证的路径,可以直接访问
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/api/**");
    }
2redis的配置
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@EnableCaching //开启缓存
@Configuration  //配置类
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

3用户在登录的时候 会跳转到自定义登录逻辑,根据用户名去查询密码,和权限。

3UserDetail
@Data
@AllArgsConstructor
@NoArgsConstructor
public  class JwtUser implements UserDetails {

    private String password;
    private  String username;
    private List<String> permissionValueList;
    private  boolean accountNonExpired;
    private  boolean accountNonLocked;
    private  boolean credentialsNonExpired;
    private  boolean enabled;


    /**
     * 返回权限的集合
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();

        for(String permissionValue : permissionValueList) {
            if(StringUtils.isEmpty(permissionValue)) continue;
//            SimpleGrantedAuthority权限的对象
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
            authorities.add(authority);
        }

        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
4自定义登录逻辑UserService
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UsersService usersService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

//        查数据库
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        Users user = usersService.getOne(wrapper);

        if (user == null){
            throw new UsernameNotFoundException("用户找不到");
        }

        /**
         * 根据username 查询 角色信息  需要加前缀  ROLE_
         * 根据角色信息 查询权限
         *
         * 角色加上前缀ROLE_  添加到 Collection<GrantedAuthority>
         *  权限直接添加到  添加到 Collection<GrantedAuthority>
         *
         *      角色是一种特殊(加前缀)的权限
         *      List<String> Roles = RoleService.selectRole(userName)
         *      List<String> Auths = AuthorityService.selectAuthority(roleId)
         *      两个list合并  注入到User中
         */

//        权限
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");


        return new User(user.getUsername(),
                new BCryptPasswordEncoder().encode(user.getPassword()),
                auths);
    }
}
验证是否登录成功
5成功后会来到UsernamePasswordAuthenticationFilter
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final ThreadLocal<Integer> rememberMe = new ThreadLocal<>();
    private final AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        super.setFilterProcessesUrl("/auth/login");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {

        // 从输入流中获取到登录的信息
        try {
            LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class);
            rememberMe.set(loginUser.getRememberMe() == null ? 0 : loginUser.getRememberMe());
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList<>())
            );
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    // 成功验证后调用的方法
    // 如果验证成功,就生成token并返回
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {

        JwtUser jwtUser = (JwtUser) authResult.getPrincipal();
        System.out.println("jwtUser:" + jwtUser.toString());
        boolean isRemember = rememberMe.get() == 1;

        String role = "";
        Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities();
        for (GrantedAuthority authority : authorities){
            role = authority.getAuthority();
        }

        String token = JwtTokenUtils.createToken(jwtUser.getUsername(), role, isRemember);
//        String token = JwtTokenUtils.createToken(jwtUser.getUsername(), false);
        // 返回创建成功的token
        // 但是这里创建的token只是单纯的token
        // 按照jwt的规定,最后请求的时候应该是 `Bearer token`
        response.setHeader("token", JwtTokenUtils.TOKEN_PREFIX + token);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        response.getWriter().write("authentication failed, reason: " + failed.getMessage());
    }
}

从输入流中获取用户信息, 然后根据用户名 权限生成token 放在请求头中 token:Bearer token的形式,然后把它返回到前端

前端会从请求头中获取到token 然后在每一次请求当中都携带这个token(相应拦截器和请求拦截器中配置,前置路由守卫)

6后端在 BasicAuthenticationFilter
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {

        String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER);
        // 如果请求头中没有Authorization信息则直接放行了
        if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }
        // 如果请求头中有token,则进行解析,并且设置认证信息
        try {
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
        } catch (TokenIsExpiredException e) {
            //返回json形式的错误信息
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            String reason = "统一处理,原因:" + e.getMessage();
            response.getWriter().write(new ObjectMapper().writeValueAsString(reason));
            response.getWriter().flush();
            return;
        }
        super.doFilterInternal(request, response, chain);
    }

    // 这里从token中获取用户信息并新建一个token
    private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) throws TokenIsExpiredException {
        String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, "");
        boolean expiration = JwtTokenUtils.isExpiration(token);
        if (expiration) {
            throw new TokenIsExpiredException("token超时了");
        } else {
            String username = JwtTokenUtils.getUsername(token);
            String role = JwtTokenUtils.getUserRole(token);
            if (username != null) {
                return new UsernamePasswordAuthenticationToken(username, null,
                        Collections.singleton(new SimpleGrantedAuthority(role))
                );
            }
        }
        return null;
    }
}

获得请求头中的token 然后去解析,获取到权限信息然后把它放到UsernamePasswordAuthenticationToken

7可选配置 ,前置过滤器
package com.example.security.filter;

import org.springframework.web.context.request.ServletWebRequest;
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;

/**
 * @author ali
 * @create 2021-08-31 18:50
 */
public class PreFilter extends OncePerRequestFilter {

    /**
     * 在认证之前处理的过滤器,比如可以判断验证码是否正确
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        filterChain.doFilter(request, response);
    }
}

8.jwtUtilis

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;

public class JwtTokenUtils {

    public static final String TOKEN_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Bearer ";

    private static final String SECRET = "jwtsecretdemo";
    private static final String ISS = "echisan";

    // 角色的key
    private static final String ROLE_CLAIMS = "rol";

    // 过期时间是3600秒,既是1个小时
    private static final long EXPIRATION = 3600L;

    // 选择了记住我之后的过期时间为7天
    private static final long EXPIRATION_REMEMBER = 604800L;

    // 创建token
    public static String createToken(String username,String role, boolean isRememberMe) {
        long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
        HashMap<String, Object> map = new HashMap<>();
        map.put(ROLE_CLAIMS, role);
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .setClaims(map)
                .setIssuer(ISS)
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .compact();
    }

    // 从token中获取用户名
    public static String getUsername(String token){
        return getTokenBody(token).getSubject();
    }

    // 获取用户角色
    public static String getUserRole(String token){
        return (String) getTokenBody(token).get(ROLE_CLAIMS);
    }

    // 是否已过期
    public static boolean isExpiration(String token) {
        try {
            return getTokenBody(token).getExpiration().before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }

    private static Claims getTokenBody(String token){
        return Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();
    }
}
9.responseUtils 返回结果

import com.baomidou.mybatisplus.extension.api.R;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;

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

public class ResponseUtil {

    public static void out(HttpServletResponse response, String  r) {
        ObjectMapper mapper = new ObjectMapper();
//        设置状态码
        response.setStatus(HttpStatus.OK.value());
//        设置内容格式
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        try {
//           让java对象转为json输出到客户端
            mapper.writeValue(response.getWriter(), r);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

10.自定义登出处理器

import com.example.base.utils.R;
import com.example.base.utils.ResponseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//退出处理器

@Component
public class TokenLogoutHandler implements LogoutHandler {
//    需要移除token
    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenLogoutHandler(TokenManager tokenManager,RedisTemplate redisTemplate) {
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
    }
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        //1 从header里面获取token
        //2 token不为空,移除token,从redis删除token
        String token = request.getHeader("token");
        if(token != null) {

            //从token获取用户名
            String username = tokenManager.getUserFromToken(token);
            redisTemplate.delete(username);

            //移除
            tokenManager.removeToken(token);
        }
        ResponseUtil.out(response, R.ok());
    }
}

2.oos增加配置

1.AuthorizationServerConfig 授权客户端配置

import com.example.oauth.service.UserService;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.RSAKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.util.JsonParser;
import org.springframework.security.oauth2.common.util.JsonParserFactory;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.approval.*;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private ClientDetailsService clientDetailsService;

	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
	PasswordEncoder passwordEncoder;

	@Autowired
	UserService userService;

	@Autowired
	@Qualifier("JwtTokenStore")
	TokenStore jwtTokenStore;

	@Autowired
	JwtAccessTokenConverter jwtAccessTokenConverter;

	@Autowired
	MyTokenEnhancer myTokenEnhancer;


	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// @formatter:off
		clients.inMemory()
			.withClient("admin")
				.authorizedGrantTypes("authorization_code", "refresh_token", "client_credentials", "password")
//				.authorizedGrantTypes("authorization_code")
				.scopes("all")
				.autoApprove(true)
//				访问令牌的有效时间
				.accessTokenValiditySeconds(60*60)
//访问令牌的有效期(单位s)
				.refreshTokenValiditySeconds(60*60)
				.secret(passwordEncoder.encode("123456"))
				.redirectUris("http://localhost:9000/login")

				.and()

				.withClient("client")
				.secret(passwordEncoder.encode("123456"))
				.authorizedGrantTypes("authorization_code", "refresh_token", "client_credentials", "password")
				.scopes("all")
				.autoApprove(true)
				.redirectUris("http://localhost:9002/login")
				.accessTokenValiditySeconds(60*60*60)
				.refreshTokenValiditySeconds(60*60*60);

	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//		获取秘钥是否需要验证  单点登录必须配置
		security.tokenKeyAccess("isAuthenticated()");
	}

	//	整合jwt
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

//		给jwt添加内容
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		List<TokenEnhancer> tokenEnhancerList = new ArrayList<>();
		tokenEnhancerList.add(myTokenEnhancer);
//		把myTokenEnhancer转为jwt
		tokenEnhancerList.add(jwtAccessTokenConverter);
		tokenEnhancerChain.setTokenEnhancers(tokenEnhancerList);

		endpoints
			.authenticationManager(this.authenticationManager)
				.userDetailsService(userService)
				.tokenStore(jwtTokenStore)
				.accessTokenConverter(jwtAccessTokenConverter)
				.tokenEnhancer(tokenEnhancerChain);
	}




//	@Bean
//	public UserApprovalHandler userApprovalHandler() {
//		ApprovalStoreUserApprovalHandler userApprovalHandler = new ApprovalStoreUserApprovalHandler();
//
//		userApprovalHandler.setClientDetailsService(this.clientDetailsService);
//		userApprovalHandler.setRequestFactory(new DefaultOAuth2RequestFactory(this.clientDetailsService));
//		return userApprovalHandler;
//	}

//	@Bean
//	public TokenStore tokenStore() {
//		JwtTokenStore tokenStore = new JwtTokenStore(new JwtAccessTokenConverter());
//		tokenStore.setApprovalStore(new TokenApprovalStore());
//		return tokenStore;
//	}
//
//	@Bean
//	public JwtAccessTokenConverter accessTokenConverter() {
//		return new JwtAccessTokenConverter();
//	}
//
//	@Bean
//	public ApprovalStore approvalStore() {
//		return new InMemoryApprovalStore();
//	}
//
//	@Bean
//	public JWKSet jwkSet() {
//		RSAKey.Builder builder = new RSAKey.Builder(KeyConfig.getVerifierKey())
//				.keyUse(KeyUse.SIGNATURE)
//				.algorithm(JWSAlgorithm.RS256)
//				.keyID(KeyConfig.VERIFIER_KEY_ID);
//		return new JWKSet(builder.build());
//	}
}
2.资源处理器
import com.sun.jndi.cosnaming.RemoteToCorba;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;

/**
 * @author ali
 * @create 2021-08-27 19:54
 */
@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .requestMatchers().antMatchers("/user/**");
    }

}

3.JwtTokenConfig配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * @author ali
 * @create 2021-08-27 21:54
 */
@Configuration
public class JwtTokenConfig {

    @Bean
    public TokenStore JwtTokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {

        return new JwtTokenStore(jwtAccessTokenConverter);
    }


    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//        设置jwt的秘钥
        jwtAccessTokenConverter.setSigningKey("xxxx");
        return jwtAccessTokenConverter;
    }
}

4.给jwt设置内容


import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Component;

import java.util.HashMap;

/**
 * @author ali
 * @create 2021-08-27 22:09
 *
 * 给jwt设置内容
 */
@Component
public class MyTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        DefaultOAuth2AccessToken defaultOAuth2AccessToken = (DefaultOAuth2AccessToken) accessToken;
        HashMap<String, Object> map = new HashMap<>();
        map.put("username", "ali");
        defaultOAuth2AccessToken.setAdditionalInformation(map);
        return defaultOAuth2AccessToken;
    }
}

4.SecurityConfig配置
package com.example.oauth.config;

import com.example.oauth.exception.JWTAccessDeniedHandler;
import com.example.oauth.exception.JWTAuthenticationEntryPoint;
import com.example.oauth.filter.JWTAuthenticationFilter;
import com.example.oauth.filter.JWTAuthorizationFilter;
import org.apache.tomcat.util.http.parser.Authorization;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.web.builders.HttpSecurity;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

/**
 *
 * http://localhost:8081/oauth/authorize?response_type=code&client_id=admin&redirect_uri=http://www.baidu.com&scope=all
 * @author ali
 * @create 2021-08-27 17:27
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests().antMatchers("/oauth/**","/login/**",
                "/logout/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll();
//                .and()
//                .authorizeRequests()
//                .antMatchers(HttpMethod.DELETE, "/tasks/**").hasRole("ADMIN");
                // 测试用资源,需要验证了的用户才能访问
//                .antMatchers("/tasks/**").authenticated()
                // 其他都放行了
//                .anyRequest().permitAll()
//                .and()
//                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
//                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
//                // 不需要session
//                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//                .and()
//                .exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
//                .accessDeniedHandler(new JWTAccessDeniedHandler());      //添加无权限时的处理

    }




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


//    @Bean
//    CorsConfigurationSource corsConfigurationSource() {
//        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//        source.registerCorsConfiguration("/**",
//                new Co
    rsConfiguration().applyPermitDefaultValues());
//        return source;
//    }
}
5.pom配置
 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>


        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>


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

        <!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.5.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-jwt -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.1.1.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt -->
        <dependency>
            <groupId>com.nimbusds</groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
            <version>9.12.1</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>


    </dependencies>

客户端pom配置

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>






        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.3.3</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 微服务鉴权心是指在微服务架构使用一个独立的鉴权心来统一管理和验证各个微服务的访问权限。而网关是作为微服务鉴权心的入口,负责对外提供服务,同时也是微服务的访问控制和认证的第一层防线。在网关配置使用Spring Security OAuth2是一种常见的实现方式。 首先,需要在网关项目引入Spring Security OAuth2的依赖。可以使用Maven或Gradle将所需的依赖加入到项目的配置文件。 接下来,需要配置Spring Security OAuth2的相关信息,包括鉴权心的认证服务器地址、客户端ID和密钥等。这些配置可以在网关项目的配置文件设置。 然后,需要配置网关的请求过滤器,用于拦截并验证请求。可以实现一个自定义的过滤器,继承自Spring Security的AbstractAuthenticationProcessingFilter类,并覆写其的相关方法。 在过滤器,需要使用OAuth2的认证流程来验证请求的合法性。可以使用RestTemplate发送请求到鉴权心的认证服务器,获取访问令牌和授权信息。 接着,可以通过OAuth2AuthenticationManager来管理认证信息,并创建并设置Spring Security的Authentication对象。可以使用自定义的处理器将认证信息传递给后续的微服务。 最后,需要对网关的接口进行权限的配置。可以使用Spring Security的注解来定义接口的访问权限,如@PreAuthorize和@Secured。 通过以上的配置,网关就可以实现对请求的鉴权和认证了。对于未经授权的请求,网关会拒绝访问,并返回相应的错误信息。对于经过鉴权验证的请求,网关会将请求转发给相应的微服务进行处理。同时,网关还可以对返回的结果进行处理和加工,以满足特定的需求。 总之,配置Spring Security OAuth2可以有效地实现微服务鉴权心之网关的权限管理和访问控制,提高系统的安全性和可维护性。 ### 回答2: 微服务鉴权心作为一个独立的服务单元,负责管理和维护整个微服务体系的权限和鉴权机制。网关是微服务系统的入口,负责接收和处理所有外部请求。 在配置Spring Security OAuth2时,我们可以将鉴权心和网关进行整合,以实现统一的认证和授权机制。在网关配置文件,我们首先需要引入Spring Security OAuth2的依赖,然后定义一些必要的配置项。 首先,我们需要配置认证服务器的地址,通常是鉴权心的地址。也就是说,所有的认证和授权请求都会转发到该地址进行处理。配置项如下: ``` security: oauth2: client: accessTokenUri: <鉴权心地址>/oauth/token userAuthorizationUri: <鉴权心地址>/oauth/authorize clientId: <客户端ID> clientSecret: <客户端密钥> ``` 其,`accessTokenUri`表示获取访问令牌的地址,`userAuthorizationUri`表示用户授权的地址,`clientId`和`clientSecret`是鉴权心针对网关颁发的客户端ID和密钥。 接下来,我们需要配置资源服务器的地址,即微服务的地址。配置项如下: ``` security: oauth2: resource: userInfoUri: <微服务地址>/user ``` 其,`userInfoUri`表示获取用户信息的地址。 最后,我们需要配置路由规则,指定哪些请求需要进行认证和授权。配置项如下: ``` spring: cloud: gateway: routes: - id: <路由ID> uri: <目标URI> predicates: - Path=/api/** filters: - TokenRelay metadata: authorization-uri: <鉴权心地址>/oauth/authorize ``` 其,`id`表示路由的唯一标识,`uri`表示目标URI,`predicates`指定路由的条件,`filters`指定过滤器,`metadata`指定认证和授权的地址。 通过以上配置,我们可以实现网关和鉴权心的整合,实现微服务系统的统一认证和授权机制。网关会将认证和授权请求转发到鉴权心进行处理,并根据鉴权心返回的结果进行相应的操作。 ### 回答3: 在微服务架构,鉴权是一个非常重要的部分,它能够确保只有经过授权的用户才能访问特定的服务。而网关作为整个系统的入口,负责转发和路由请求,需要配置Spring SecurityOAuth2来实现鉴权。 首先,需要在网关服务添加Spring SecurityOAuth2的相关依赖,并在配置文件开启相关功能。接着,需要创建一个自定义的鉴权过滤器,该过滤器会在请求到达网关之后进行鉴权操作。 在过滤器,首先需要配置一个OAuth2的资源服务。这个资源服务可以通过配置一个TokenStore来获取和保存令牌信息。然后,可以配置一个JwtAccessTokenConverter来验证和解析令牌。同时,需要配置一个ResourceServerTokenServices,该服务会验证令牌的正确性,是否过期等信息。 接下来,在过滤器需要配置spring security的认证管理器,该管理器会根据请求头的令牌信息来进行用户的鉴权操作。可以使用一个自定义的UserDetailsService来获取用户的权限信息,并将权限信息添加到SecurityContext,以便后续的鉴权操作。 最后,在过滤器,可以配置一些具体的鉴权规则,比如某些URL需要特定的角色才能访问,可以用.antMatchers().hasRole()的方式进行配置。 通过以上步骤,就可以实现网关的鉴权功能。当用户发起请求时,网关会根据请求头的令牌信息进行鉴权,只有经过授权的用户才能访问特定的服务。这样可以确保整体系统的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值