项目由springboot1.5.X升级到springboot2.0.0后,导致各组件API以及依赖包发生了变化。
完整项目demo:https://gitee.com/zkane/springboot2-oauth2.git
Spring Security 从入门到进阶系列教程网址:http://www.spring4all.com/article/428
https://blog.csdn.net/qq_19671173/article/details/79748422
http://wiselyman.iteye.com/blog/2411813
org.springframework.boot
spring-boot-starter-security
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.0.0.RELEASE
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
解决方案:自己编写一个MyRedisTokenStore,复制RedisTokenStore类中代码,并将代码中conn.set(accessKey, serializedAccessToken)修改为conn.stringCommands().set(accessKey, serializedAccessToken);
解决方案:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 1
corsConfiguration.addAllowedOrigin("*");
// 2
corsConfiguration.addAllowedHeader("*");
// 3
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// step 4
source.registerCorsConfiguration("/**", buildConfig());
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
@SpringBootApplication
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
/**
* 解决前后端分离跨域问题
*
* @return
*/
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
解决方案:授权模式使用password的方式
(Y2xpZW50OnNlY3JldA==为64编码,格式:client:secret)
bcrypt - BCryptPasswordEncoder (Also used for encoding)
ldap - LdapShaPasswordEncoder
MD4 - Md4PasswordEncoder
MD5 - new MessageDigestPasswordEncoder(“MD5”)
noop - NoOpPasswordEncoder
pbkdf2 - Pbkdf2PasswordEncoder
scrypt - SCryptPasswordEncoder
SHA-1 - new MessageDigestPasswordEncoder(“SHA-1”)
SHA-256 - new MessageDigestPasswordEncoder(“SHA-256”)
sha256 - StandardPasswordEncoder
@EnableOAuth2Sso
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/index")
.permitAll()
.anyRequest()
.authenticated();
}
}
package com.bici.controller;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* @author: [email protected]
* @date: 2018/4/17
*/
@RestController
@RequestMapping("/client")
@Secured("ROLE_ADMIN")
public class ClientController {
@GetMapping("/user")
public Authentication getUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication;
}
@GetMapping("/index")
@Secured("ROLE_USER")
public String index() {
return "index";
}
}
import com.bici.encrypt.EncryptUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Qualifier("userDetailsService")
@Autowired
private UserDetailsService userDetailsService;
@Override
protected UserDetailsService userDetailsService() {
// 自定义用户信息类
return this.userDetailsService;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder(){
@Override
public String encode(CharSequence charSequence) {
// 加密
return EncryptUtil.hashPasswordAddingSalt(charSequence.toString());
}
@Override
public boolean matches(CharSequence charSequence, String s) {
// 密码校验
return EncryptUtil.isValidPassword(charSequence.toString(), s);
}
}) ;
}
}
可以在这个地方将token和username存入到缓存中,然后如果需要强制某个用户下线时,通过username从缓存中找到token,调用ConsumerTokenServices
的revokeToken(token)
方法。
package com.zkane.aspect;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
/**
* @author: [email protected]
* @review:
* @date: 2018/8/24
*/
@Aspect
@Component
public class TokenAspect {
@Pointcut("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
private void token() {
}
@AfterReturning(returning = "obj", pointcut = "token()")
public void doAfterReturning(ResponseEntity obj) throws Exception {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 打印用户名
System.out.println(request.getParameter("username"));
// 打印token
System.out.println(obj.getBody().getValue());
}
}
CREATE TABLE `oauth_client_details` (
`client_id` varchar(256) NOT NULL,
`resource_ids` varchar(256) DEFAULT NULL,
`client_secret` varchar(256) DEFAULT NULL,
`scope` varchar(256) DEFAULT NULL,
`authorized_grant_types` varchar(256) DEFAULT NULL,
`web_server_redirect_uri` varchar(256) DEFAULT NULL,
`authorities` varchar(256) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(4096) DEFAULT NULL,
`autoapprove` varchar(256) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO oauth_client_details (
client_id,
resource_ids,
client_secret,
scope,
authorized_grant_types,
web_server_redirect_uri,
authorities,
access_token_validity,
refresh_token_validity,
additional_information,
autoapprove
)
VALUES
(
'client',
NULL,
'{noop}secret',
'all',
'password,authorization_code,refresh_token,implicit,client_credentials',
NULL,
NULL,
NULL,
NULL,
NULL,
'true'
);
@Configuration
@EnableAuthorizationServer
public class ServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
}
{
"error": "invalid_grant",
"error_description": "Bad credentials"
}
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
return messageSource;
}
}
{
"error": "invalid_grant",
"error_description": "账号未注册,请联系管理员"
}