Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。
<!-- ==========security dependency ==============-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
@Configuration
@EnableWebSecurity //开启WebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定义用户服务类,用于用户名、密码校验,权限获取
*/
@Resource(name = "userDetailsService")
private UserDetailsService userDetailsService;
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 安全拦截机制
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/**").permitAll() //oauth下的所有方法,无须认证
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
/**
* 密码加密策略
* @return
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
自定义用户服务类,继承UserDetailsService,重写loadUserByUsername 方法,根据自身业务修改代码
@Service(value = "userDetailsService")
public class UserServiceImpl implements UserDetailsService {
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 这里需要写根据用户名查询用户逻辑。。。。。。
UserDTO user = new UserDTO();
user.setUserId("1");
user.setUsername("admin");
user.setMobile("188103956897");
user.setStatus(6);
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
UserDTO userDTO = new UserDTO();
// 为了增强jwt令牌内容,可以将整个对象转json存放到username中
userDTO.setUsername(JSON.toJSONString(user));
// userDTO.setUsername("admin");
userDTO.setPassword(bCryptPasswordEncoder.encode("123"));
return userDTO;
}
}
配合UserServiceImpl使用,返回用户对象
@Getter
@Setter
public class UserDTO implements UserDetails {
private String userId;
private String username;
private String password;
private String mobile;
private String realName;
private String companyId;
private Integer status;
public UserDTO() {
}
public UserDTO(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return null;
}
@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;
}
}
OAuth 2.0是目前最流行的授权机制,用来授权第三方应用,获取用户数据。
允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容
常用遇到的场景就是其他软件登录的时候,不想注册账户,可以选择用QQ、微信等方式进行登录,这个时候不需要输入账户、密码。而是需要扫描,在QQ或者微信中进行确认即可
是最复杂、最安全的授权方式
1、先获取授权码
2、根据授权码再去申请token
常用于单页面应用,没有复杂的后台,直接通过url形式获取token
常用于内部系统,完全可以信任的情况,根据用户名、密码获取token
最简单最不安全的方式,根据客户端id、授权密码就可以获取token
JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公 钥/私钥对来签名,防止被篡改
1、jwt基于json,非常方便解析。
2、可以在令牌中自定义丰富的内容,易扩展。
3、通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
4、资源服务使用JWT可不依赖认证服务即可完成授权。
1、JWT令牌较长,占存储空间比较大。
<!-- ==========security-oauth2 dependency ==============-->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
/**
* 开启授权服务器
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
/**
* 指定哪些资源是需要授权验证的
*/
private static final String RESOURCE_ID = "ALL";
/**
* 设置jwt加密key
*/
private static final String JWT_SIGNING_KEY = "jwt_MC43A6m0Xt9jUIV";
/**
* 认证方式
*/
@Autowired
private AuthenticationManager authenticationManager;
/**
* 自定义用户服务
*/
@Resource(name = "userDetailsService")
private UserDetailsService userDetailsService;
// @Autowired
// private CustomWebResponseExceptionTranslator customWebResponseExceptionTranslator;
/**
* 配置客户端对应授权方式及客户端密码
* 当前使用内存模式
* @param configurer
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
// 用 BCrypt 对密码编码
String secret = new BCryptPasswordEncoder().encode("MC43A6m0Xt9jUIV");
//配置1个客户端,一个用于password认证
configurer.inMemory() // 使用in-memory存储
.withClient("weixin") //client_id用来标识客户的Id 客户端1
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes("password", "refresh_token") //允许授权类型 密码授权模式
.scopes("read","write") //允许授权范围
.authorities("oauth2") //客户端可以使用的权限
.secret(secret) //secret客户端安全码
.accessTokenValiditySeconds(1*60*60) //token 时间秒
.refreshTokenValiditySeconds(6*60*60); //刷新token 时间 秒
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.accessTokenConverter(accessTokenConverter())
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) //支持GET POST 请求获取token
.userDetailsService(userDetailsService) //必须注入userDetailsService否则根据refresh_token无法加载用户信息
// .exceptionTranslator(customWebResponseExceptionTranslator)
.reuseRefreshTokens(true); //开启刷新token
}
/**
* 认证服务器的安全配置
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()") //isAuthenticated():排除anonymous isFullyAuthenticated():排除anonymous以及remember-me
.allowFormAuthenticationForClients(); //允许表单认证
}
/**
* jwt令牌增强,添加加密key
*/
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(JWT_SIGNING_KEY);
return converter;
}
/**
* 使用JWT存储令牌信息
* @return
*/
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
/**
* token认证服务
*/
@Bean
public ResourceServerTokenServices tokenService() {
// 授权服务和资源服务在统一项目内,可以使用本地认证方式,如果再不同工程,需要使用远程认证方式
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
/**
* 资源服务器配置
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "ALL";
@Autowired
private ResourceServerTokenServices tokenServices;
// @Autowired
// private LogoutSuccessHandler customLogoutSuccessHandler;
// @Autowired
// private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
/**
* 验证令牌配置
* RESOURCE_ID 必须和授权服务器中保持一致
* @param config
*/
@Override
public void configure(ResourceServerSecurityConfigurer config) {
config.resourceId(RESOURCE_ID)
.tokenServices(tokenServices)
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/users/**").authenticated() //配置users访问控制,必须认证过后才可以访问
.antMatchers("/test/**").permitAll() //配置test无须认证,可以匿名访问
.anyRequest().authenticated();
// .and()
// .exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint) //认证失败的业务处理
// .and()
// .logout()
// .logoutUrl("/oauth/logout")
// .logoutSuccessHandler(customLogoutSuccessHandler); //退出成功的业务处理
}
// @Bean
// public LogoutSuccessHandler customLogoutSuccessHandler(){
// return new CustomLogoutSuccessHandler();
// }
// @Bean
// public AuthenticationEntryPoint customAuthenticationEntryPoint(){
// return new CustomAuthenticationEntryPoint();
// }
}
import com.akm.security.security.model.UserDTO;
import com.alibaba.fastjson.JSON;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class SystemController {
/**
* 访问时,需要携带token,进行认证
*/
@GetMapping("/user/list")
public List getUser(){
List users = new ArrayList<>();
users.add(new UserDTO("张三","123456"));
users.add(new UserDTO("李四","123456"));
users.add(new UserDTO("张五","123456"));
return users;
}
/**
* 支持匿名访问,无须携带token
*/
@GetMapping("/test/list")
public List getTestUser(){
List users = new ArrayList<>();
users.add(new UserDTO("张三","123456"));
users.add(new UserDTO("李四","123456"));
users.add(new UserDTO("张五","123456"));
return users;
}
/**
* 获取当前登录用户信息
*
* @param authentication
* @return
*/
@RequestMapping("/info")
public Object info(OAuth2Authentication authentication) {
// 需要配合UserServiceImpl--》userDTO.setUsername(JSON.toJSONString(user))使用
UserDTO ddo = JSON.parseObject(authentication.getPrincipal().toString(),UserDTO.class);
// SecurityContextHolder 和 OAuth2Authentication都可以获取当前用户信息
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return "获取到了用户资源:"+ddo.getUsername()+"------"+ddo.getUsername()+"----"+ddo.getMobile();
}
}
访问方法时,可以使用postman进行接口访问
访问URL:post形式访问
http://localhost:8081/oauth/token
携带参数:要和AuthorizationServerConfiguration–》configurer.inMemory() 中设置的参数对应上
client_id–》客户端id client_secret–>客户端密码 grant_type–》认证方式,password为密码方式
username–》用户名 password–》密码
client_id: weixin
client_secret: MC43A6m0Xt9jUIV
grant_type: password
username: admin
password: 123
刷新token,是指已经获取到token、refresh_token
访问URL:post形式访问
http://localhost:8081/oauth/token
携带参数:
client_id–》客户端id client_secret–>客户端密码 grant_type–》refresh_token
refresh_token–》获取token时,返回的refresh_token值
client_id: weixin
client_secret: MC43A6m0Xt9jUIV
grant_type: refresh_token
refresh_token : xxxxxxxxxxxxxxxxxxxxxxxxxxxx
客户端获取到token时,访问需要验证的资源时,需要在headers中携带参数
访问URL:
http://localhost:8081/server/user/list
携带参数:
Authorization —》固定参数,对应参数为token
bearer —》token传递策略
Authorization: bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。
Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
Multicast 注册中心
Zookeeper 注册中心(推荐使用)
Nacos 注册中心 (阿里开源组件,阿里内部使用)
Redis 注册中心
Simple 注册中心
com.alibaba.spring.boot
dubbo-spring-boot-starter
2.0.0
com.101tec
zkclient
0.10
com.alibaba
dubbo-registry-nacos
2.6.7
application.properties
server.port=8888
dubbo.application.name=dubbo-user
# 组网形式
#dubbo.registry.address=multicast://224.5.6.7:1234
# zookeeper 注册中心
#dubbo.registry.address=zookeeper://127.0.0.1:2181
# nacos 注册中心
dubbo.registry.address=nacos://127.0.0.1:8848
# 简单注册中心
#dubbo.registry.address=127.0.0.1:9090
# 直连形式
#dubbo.registry.address=N/A
# 对外开放接口
dubbo.protocol.name=dubbo
dubbo.protocol.host=192.168.1.11
dubbo.protocol.port=20888
application.properties
server.port=9999
dubbo.application.name=dubbo-page
# 组网形式(多网卡或者开启虚拟机网卡时,容易出现问题)
#dubbo.registry.address=multicast://224.5.6.7:1234
# zookeeper 注册中心
#dubbo.registry.address=zookeeper://127.0.0.1:2181
# nacos 注册中心
dubbo.registry.address=nacos://127.0.0.1:8848
# 简单注册中心
#dubbo.registry.address=127.0.0.1:9090
# 直连形式
#dubbo.registry.address=N/A
使用dubbo框架的时候,需要将提供服务的接口提取到公共API工程中,消费端也需要引用API工程
接口和引用的Bean都要保持一致
UserService
public interface UserService {
/**
* 根据ID查询用户信息
* @param user
* @return
*/
public String getUserById(User user);
}
User
public class User implements Serializable {
private String id;
private String name;
public User() {
}
public User(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
UserServiceImpl,@Service将UserServiceImpl对外发布
import com.akm.dubbo.api.service.UserService;
import com.akm.dubbo.api.bean.User;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* 将UserServiceImpl交给dubbo管理
*/
@Component
@Service
public class UserServiceImpl implements UserService {
/**
* 根据ID查询用户信息
* @param user
* @return
*/
@Override
public String getUserById(User user) {
List list = new ArrayList<>();
list.add(new User("1","张三"));
list.add(new User("2","李四"));
System.out.println(JSON.toJSONString(user));
System.out.println(JSON.toJSONString(list));
return "用户ID:"+user.getId();
}
}
DubboUserApplication ,@EnableDubbo 开启Dubbo
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubbo
@SpringBootApplication
public class DubboUserApplication {
public static void main(String[] args) {
SpringApplication.run(DubboUserApplication.class, args);
System.out.println("dubbo 服务提供者启动成功************");
}
}
PageController
@Reference 注解会自动将远程dubbo中的userService类进行注入
import com.akm.dubbo.api.bean.User;
import com.akm.dubbo.api.service.UserService;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PageController {
@Reference(check = false) //,url = "dubbo://127.0.0.1:20888"
UserService userService;
@GetMapping("/user")
public String getUser(){
User user = new User("123456","这就是duboo");
userService.getUserById(user);
return "这就是用户信息";
}
}
DubboPageApplication , @EnableDubbo开启Dubbo功能
@EnableDubbo
@SpringBootApplication
public class DubboPageApplication {
public static void main(String[] args) {
SpringApplication.run(DubboPageApplication.class, args);
System.out.println("Duboo 服务消费者启动成功************");
}
}
Swagger2 是一个规范和完整的框架,用于生成、描述、调用和可视化Restful风格的web服务 ,可以通过在类、方法上添加注解的形式,生成在线API。节省了很多前后端接口沟通的时间!
<!-- ==========Swagger2 dependency ==============-->
<dependencies>
<!--导入Swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--导入Swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
</dependencies>
@Configuration
@EnableSwagger2 // 开启Swagger
@Profile("dev") // 只有在dev开发状态,才对外开启api功能
public class AcmeSwagger2Config {
@Bean
public Docket createRestApi(Environment environment){
// // 设置要显示Swagger的环境:开发环境、测试环境
// Profiles profiles= Profiles.of("dev","test");
// //获取项目的环境,判断是否处在自己设定的环境当中
// boolean enalbeFlag= environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
// .enable(true)
.select()
// 所有ApiOperation注解的方法,生成接口文档
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("物联网基础平台")
.description("物联网基础平台 API 文档")
.termsOfServiceUrl("http://localhost:8080")
.version("1.0")
.build();
}
}
/**
* 为swagger建立新的静态文件路径映射
*/
@Configuration
public class AcmeSwaggerAutoConfiguration extends WebMvcConfigurerAdapter {
/**
* 因为swagger-ui.html 是在springfox-swagger-ui.jar里的,
* 修改了路径后,Spring Boot不会自动把/swagger-ui.html这个路径映射到对应的目录META-INF/resources/下面,
* 所以需要修改springboot配置类,为swagger建立新的静态文件路径映射就可以了
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
类上使用注解 @Api(tags = “Hello控制类”)
方法上使用注解 @ApiOperation(“hello测试”)
@Api(tags = "Hello控制类")
@RestController
@RequestMapping("/server")
public class HelloController {
@ApiOperation("hello测试")
@ResponseBody
@GetMapping(value="/hello")
public String hello(){
return "测试Swagger";
}
}
实体类使用注解@ApiModel(“用户实体类”)
实例类中的属性使用注解@ApiModelProperty(“用户名”)
@ApiModel("用户实体类")
public class User {
@ApiModelProperty("用户名")
public String username;
@ApiModelProperty("密码")
public String password;
}