目录
一、添加依赖
二、添加授权中心配置类
三、服务安全配置类
四、SQL静态类
五、登录逻辑的实现类
六、通过APIPost获取JWT
七、附加,密钥的获取
八、资源服务器的访问控制
一、添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
这里主要的依赖就是oauth2。
配置文件如下:
server:
port: 9999
spring:
application:
name: authorization-server
cloud:
nacos:
discovery:
server-addr: nacos-server:8848
username: nacos
password: nacos
datasource:
url: jdbc:mysql://mysql-server:3307/dragon-backstage?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: WXL1214??
driver-class-name: com.mysql.cj.jdbc.Driver
二、添加授权中心配置类
这里继承授权中心的适配器,通过PasswordEncoder来加密密码,通过AuthenticationManager来进行验证管理,通过jwt实现时需要重写一下方法。
package com.dragonwu.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
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.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.KeyStoreKeyFactory;
@EnableAuthorizationServer //开启授权服务器功能
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;//验证管理器
@Qualifier("userServiceDetailServiceImpl")
@Autowired
private UserDetailsService userDetailsService;
/**
* 添加第三方客户端
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("dragon-api") //第三方客户端名称
.secret(passwordEncoder.encode("dragon-secret")) //第三方客户端的密钥
.scopes("all") //第三方客户端的授权范围
.authorizedGrantTypes("password","refresh_token")//密码授权,通过refresh_token来为过期的token获取新的token数据
.accessTokenValiditySeconds(7*24*3600) //token的有效期
.refreshTokenValiditySeconds(30*24*3600) //refresh_token的有效期
.and()
.withClient("inside-app")
.secret(passwordEncoder.encode("inside-secret"))
.authorizedGrantTypes("client_credentials")
.scopes("all")
.accessTokenValiditySeconds(Integer.MAX_VALUE) ;
super.configure(clients);
}
/**
* 配置验证管理器,UserDetailService
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenStore(jwtTokenStore()) //采用JWT存储token
.tokenEnhancer(jwtAccessTokenConverter()); //转换器,将数据转为json存储
super.configure(endpoints);
}
private TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter tokenConverter=new JwtAccessTokenConverter();
//加载服务器的私钥
ClassPathResource classPathResource=new ClassPathResource("dragonbackstage.jks");
KeyStoreKeyFactory keyStoreKeyFactory=new KeyStoreKeyFactory(classPathResource,"dragonbackstage".toCharArray());
tokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("dragonbackstage","dragonbackstage".toCharArray()));
return tokenConverter;
}
}
三、服务安全配置类
这里用到了SpringSecurity来保证服务器安全,具体实现是继承网络安全适配器,实例化好相应对象放到bean里。
package com.dragonwu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
}
@Bean
protected AuthenticationManager authenticationManager() throws Exception{
return super.authenticationManager();
}
/**
* 密码加密
* @return passwordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String encode = bCryptPasswordEncoder.encode("123456");
System.out.println(encode);
}
}
四、SQL静态类
package com.dragonwu.constant;
/**
* 登录的常量
*/
public class LoginConstant {
/**
* 后台管理人员
*/
public static final String ADMIN_TYPE = "admin_type" ;
/**
* 普通的用户
*/
public static final String MEMBER_TYPE = "member_type" ;
/**
* 使用用户名查询后台用户
*/
public static final String QUERY_ADMIN_SQL=
"SELECT `id`,`username`,`pass_word`,`user_status` FROM sys_user WHERE username=?";
/**
* 普通用户查询SQL
*/
public static final String QUERY_MEMBER_SQL=
"SELECT `id`,`pass_word`,`user_status` FROM `user` WHERE mobile=? or email=?";
}
五、登录逻辑的实现类
这里是登录业务类,通过继承UserDetailsService来实现,最终返回UserDetails对象。
package com.dragonwu.service.impl;
import com.dragonwu.constant.LoginConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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 org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
@Service
public class UserServiceDetailServiceImpl implements UserDetailsService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ServletRequestAttributes requestAttributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String loginType=requestAttributes.getRequest().getParameter("login_type");//区分是后台人员还是普通用户
if(StringUtils.isEmpty(loginType)){
throw new AuthorizationServiceException("登录类型不能为null");
}
UserDetails userDetails=null;
try{
switch (loginType){
case LoginConstant.ADMIN_TYPE:
userDetails=loadSysUserByUsername(username);
break;
case LoginConstant.MEMBER_TYPE:
userDetails=loadMemberUserByUsername(username);
break;
default:
throw new AuthorizationServiceException("暂不支持的登录方式:"+loginType);
}
}catch (IncorrectResultSizeDataAccessException e){//我们的用户不存在
throw new UsernameNotFoundException("用户名"+username+"不存在");
}
return userDetails;
}
/**
* 后台管理员登录
* @return userDetails
*/
private UserDetails loadSysUserByUsername(String username){
//1、使用用户名查询用户
//2、查询这个用户对应的权限
//3、封装成一个UserDetails对象,返回
return null;
}
/**
* 普通用户登录
* @return userDetatils
*/
private UserDetails loadMemberUserByUsername(String username){
return jdbcTemplate.queryForObject(LoginConstant.QUERY_MEMBER_SQL, new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
//判断用户是否存在
if(resultSet.wasNull()){
throw new UsernameNotFoundException("用户:"+username+"不存在");
}
long id=resultSet.getLong("id");//用户的id
String password=resultSet.getString("pass_word");//普通用户的登录密码
int status=resultSet.getInt("user_status");//会员的状态
return new User(
String.valueOf(id),
password,
status==1,
true,
true,
true,
Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))
);
}
},username,username);
}
}
六、通过APIPost获取JWT
这里填写你的基础认证名称和密码。
添加一下参数。
获取到token。
JWT已经获取到了。
也可以通过refresh_token来对旧的token进行刷新
?下面开始获取新的token值:
?获取新的token成功:
模拟内置服务器之间的访问:
?
?访问结果,此时获取到的token是没有用户信息的:
七、附加,密钥的获取
?打开cmd输入如下:
keytool -genkeypair -alias dragonbackstage -keyalg RSA -keypass dragonbackstage -keystore dragonbackstage.jks -validity 365 -storepass dragonbackstage
?之后会生成密钥。
获取到密钥以后,再输入一下内容
keytool -list -rfc --keystore dragonbackstage.jks | openssl x509 -inform pem -pubkey
便可以获取到公钥。
交私钥放到resource目录下即可:
八、资源服务器的访问控制
添加资源服务器的配置如下:
package com.dragonwu.config.resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
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.util.FileCopyUtils;
import java.nio.charset.StandardCharsets;
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.sessionManagement().disable()
.authorizeRequests()
.antMatchers(
"/column/test",
"/admin/login",
"/swagger-resources/configuration/ui",//用来获取支持的动作
"/swagger-resources",//用来获取api-docs的URI
"/swagger-resources/configuration/security",//安全选项
"/swagger-ui.html"
).permitAll()
.antMatchers("/**").authenticated()
.and().headers().cacheControl();
}
/**
* 设置公钥
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(jwtTokenStore());
}
private TokenStore jwtTokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean // 放在ioc容器的
public JwtAccessTokenConverter accessTokenConverter() {
//resource 验证token(公钥) authorization 产生 token (私钥)
JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
String s = null;
try {
ClassPathResource classPathResource = new ClassPathResource("dragonbackstage.txt");
byte[] bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
s = new String(bytes, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
tokenConverter.setVerifierKey(s);
return tokenConverter;
}
}
这里的公钥和之前的密钥是一对,放到resource目录下:
有了这样一个配置类,访问就可以被控制了。
|