Создайте сервер oauth2, включая серверы аутентификации, авторизации и ресурсов.
использованная литература:
блог woo woo woo.cn на.com/invoice2952/afraid/89…
Официальная документация Spring OAuth2
Эта статья разделена на две части
- Первая часть относительно проста, информация о клиенте и информация о пользователе фиксируются в программе, а токен хранится в памяти.
- Вторая часть считывает информацию о пользователе из базы данных и использует jwt для создания токена.
адрес проекта:GitHub.com/like this/spr…
ветка oauth
1. Упрощенная версия
Используйте Spring Initializr для создания нового проекта и проверьте следующие три параметра.
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
//只需要引用这一个
//集成了spring-security-oauth2 spring-security-jwt spring-security-oauth2-autoconfigure
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Настроить весеннюю безопасность
Новый класс WebSecurityConfig наследует WebSecurityConfigurerAdapter и добавляет аннотацию @Configuration @EnableWebSecurity для перезаписи трех методов. Код выглядит следующим образом, а подробности объясняются ниже кода.
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserServiceDetail userServiceDetail;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userServiceDetail).passwordEncoder(passwordEncoder());
//内存存储
// auth
// .inMemoryAuthentication()
// .passwordEncoder(passwordEncoder())
// .withUser("user")
// .password(passwordEncoder().encode("user"))
// .roles("USER");
}
/**
* 配置了默认表单登陆以及禁用了 csrf 功能,并开启了httpBasic 认证
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http // 配置登陆页/login并允许访问
.formLogin().permitAll()
// 登出页
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
// 其余所有请求全部需要鉴权认证
.and().authorizeRequests().anyRequest().authenticated()
// 由于使用的是JWT,我们这里不需要csrf
.and().csrf().disable();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
В основном объяснить
protected void configure(AuthenticationManagerBuilder auth) throws Exception
Этот метод используется для проверки информации о пользователе. Сопоставьте имя пользователя и пароль, введенные во внешнем интерфейсе, с базой данных, и только при наличии этого пользователя аутентификация будет успешной. мы вводимUserServiceDetail
, функция этой службы заключается в проверке..passwordEncoder(passwordEncoder())
расшифровывается солью.
UserServiceDetail
ДостигнутоUserDetailsService
интерфейс, поэтому вам нужно реализовать единственный метод
package zcs.oauthserver.service;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 zcs.oauthserver.model.UserModel;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceDetail implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE"));
return new UserModel("user","user",authorities);
}
}
Здесь функция сначала реализуется с поддельными параметрами, а база данных добавляется позже.
Параметр s — это имя пользователя, введенное во внешнем интерфейсе, через которого осуществляется поиск в базе данных, получаются пароль и права доступа, и, наконец, эти три данных инкапсулируются вUserDetails
Возвращается в реализующем классе интерфейса. Инкапсулированные здесь классы можно использоватьorg.springframework.security.core.userdetails.User
или реализовать самостоятельноUserDetails
интерфейс.
UserModel
выполнитьUserDetails
интерфейс
package zcs.oauthserver.model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Collection;
import java.util.List;
public class UserModel implements UserDetails {
private String userName;
private String password;
private List<SimpleGrantedAuthority> authorities;
public UserModel(String userName, String password, List<SimpleGrantedAuthority> authorities) {
this.userName = userName;
this.password = new BCryptPasswordEncoder().encode(password);;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
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;
}
}
Добавьте имя пользователя, пароль и полномочия.Последний хранит список разрешений пользователя, то есть, к каким ресурсам у пользователя есть права доступа.соление пароля.
Настройка сервера аутентификации Oauth2
Новый класс конфигурации AuthorizationServerConfig наследует AuthorizationServerConfigurerAdapter и добавляет @Configuration Аннотация @EnableAuthorizationServer указывает на сервер аутентификации.
Переопределить три функции
-
ClientDetailsServiceConfigurer
: используется для настройки службы сведений о клиенте, здесь инициализируется информация о клиенте, вы можете записать здесь сведения о клиенте или сохранить и получить данные через базу данных. Клиент обращается к стороннему приложению -
AuthorizationServerSecurityConfigurer
: ограничения безопасности, используемые для настройки конечной точки токена. -
AuthorizationServerEndpointsConfigurer
: доступ к конечным точкам и службам токенов для настройки авторизации и токенов.
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
//从WebSecurityConfig加载
@Autowired
private AuthenticationManager authenticationManager;
//内存存储令牌
private TokenStore tokenStore = new InMemoryTokenStore();
/**
* 配置客户端详细信息
*
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
//客户端ID
.withClient("zcs")
.secret(new BCryptPasswordEncoder().encode("zcs"))
//权限范围
.scopes("app")
//授权码模式
.authorizedGrantTypes("authorization_code")
//随便写
.redirectUris("www.baidu.com");
// clients.withClientDetails(new JdbcClientDetailsService(dataSource));
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager);
}
/**
* 在令牌端点定义安全约束
* 允许表单验证,浏览器直接发送post请求即可获取tocken
* 这部分写这样就行
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 开启/oauth/token_key验证端口无权限访问
.tokenKeyAccess("permitAll()")
// 开启/oauth/check_token验证端口认证权限访问
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
}
Данные клиента также используются для тестирования, а база данных будет добавлена позже. Служба токенов временно хранится в памяти, а jwt будет добавлен позже.
Самая важная функция - сначала реализовать функцию, а сложные вещи добавляются шаг за шагом.
Настроить сервер ресурсов
Сервер ресурсов также является служебной программой, то есть сервером, к которому необходимо получить доступ.
Новый ResourceServerConfig наследует ResourceServerConfigurerAdapter.
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
// antMatcher表示只能处理/user的请求
.antMatcher("/user/**")
.authorizeRequests()
.antMatchers("/user/test1").permitAll()
.antMatchers("/user/test2").authenticated()
// .antMatchers("user/test2").hasRole("USER")
// .anyRequest().authenticated()
;
}
}
ResourceServerConfigurerAdapter
Значение по умолчанию Order равно 3, меньше чемWebSecurityConfigurerAdapter
, чем меньше значение, тем выше приоритет
оResourceServerConfigurerAdapter
иWebSecurityConfigurerAdapter
Подробнее см.
woo woo Краткое описание.com/afraid/share 1194 руб 8…
Новый пользовательский контроллер
@RestController
public class UserController {
@GetMapping("/user/me")
public Principal user(Principal principal) {
return principal;
}
@GetMapping("/user/test1")
public String test() {
return "test1";
}
@GetMapping("/user/test2")
public String test2() {
return "test2";
}
}
контрольная работа
- получить код
доступ через браузер
http://127.0.0.1:9120/oauth/authorize?client_id=zcs&response_type=code&redirect_uri=www.baidu.com
, а затем прыгнуть с целевой страницы,
В адресной строке появится страница обратного звонка с параметром codehttp://127.0.0.1:9120/oauth/www.baidu.com?code=FGQ1jg
- получить жетон
доступ почтальона
http://127.0.0.1:9120/oauth/token?code=FGQ1jg&grant_type=authorization_code&redirect_uri=www.baidu.com&client_id=zcs&client_secret=zcs
, код введите только что полученный код, используйте запрос POST - доступ к ресурсам
/user/test2 — это защищенный ресурс, доступ к которому осуществляется через токен
2. Обновленная версия
JWT
Многие будут сравнивать JWT и OAuth2, на самом деле это совершенно разные понятия и несопоставимы.
JWT — это протокол аутентификации, который предоставляет метод выдачи маркеров доступа и проверки выпущенных подписанных маркеров доступа.
OAuth2 — это структура авторизации, предоставляющая подробный механизм авторизации.
Spring Cloud OAuth2 интегрирует JWT для управления токенами, поэтому его удобно использовать.
JwtAccessTokenConverter
Это преобразователь, используемый для создания маркера, а маркер маркера подписан по умолчанию, и серверу ресурсов необходимо проверить эту подпись. Шифрование и проверка подписи здесь включают два метода:
Симметричное шифрование, асимметричное шифрование (открытый ключ)
Симметричное шифрование требует, чтобы сервер авторизации и сервер ресурсов хранили одно и то же значение ключа, в то время как асимметричное шифрование может использовать шифрование ключа для предоставления открытого ключа серверу ресурсов для проверки подписи.В этой статье используется асимметричное шифрование.
Создайте сертификат jks с помощью инструмента jdk, войдите в bin каталога установки jdk через cmd и выполните команду
keytool -genkeypair -alias oauth2-keyalg RSA -keypass mypass -keystore oauth2.jks -storepass mypass
Файл oauth2.jks будет сгенерирован в текущем каталоге и помещен в каталог ресурсов.
Maven по умолчанию не загружает файлы в каталог ресурсов, поэтому его необходимо настроить в pom.xml и добавить при сборке.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
Изменить часть кода в исходном AuthorizationServerConfig
@Autowired
private TokenStore tokenStore;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// endpoints.tokenStore(tokenStore)
// .authenticationManager(authenticationManager);
endpoints.authenticationManager(authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter())
.tokenStore(tokenStore);
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 非对称加密算法对token进行签名
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
final JwtAccessTokenConverter converter = new CustomJwtAccessTokenConverter();
// 导入证书
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(new ClassPathResource("oauth2.jks"), "mypass".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("oauth2"));
return converter;
}
jwtAccessTokenConverter
метод имеетCustomJwtAccessTokenConverter
класс, который наследуетJwtAccessTokenConverter
, пользовательская добавленная дополнительная информация о токене
/**
* 自定义添加额外token信息
*/
public class CustomJwtAccessTokenConverter extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken defaultOAuth2AccessToken = new DefaultOAuth2AccessToken(accessToken);
Map<String, Object> additionalInfo = new HashMap<>();
UserModel user = (UserModel)authentication.getPrincipal();
additionalInfo.put("USER",user);
defaultOAuth2AccessToken.setAdditionalInformation(additionalInfo);
return super.enhance(defaultOAuth2AccessToken,authentication);
}
}
Security
Предыдущий логин был с поддельными данными, а теперь он проверяется путем подключения к базе данных.
Создайте три таблицы, пользователь хранит учетные записи и пароли пользователей, роль хранит роли, а пользовательская роль хранит роли пользователей.
пользовательская таблица
таблица ролей
таблица user_role
Используйте mybatis-plus для генерации кода и преобразования предыдущегоUserServiceDetail
иUserModel
UserServiceDetail
@Service
public class UserServiceDetail implements UserDetailsService {
private final UserMapper userMapper;
private final RoleMapper roleMapper;
@Autowired
public UserServiceDetail(UserMapper userMapper, RoleMapper roleMapper) {
this.userMapper = userMapper;
this.roleMapper = roleMapper;
}
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("username", s);
User user = userMapper.selectOne(userQueryWrapper);
if (user == null) {
throw new RuntimeException("用户名或密码错误");
}
user.setAuthorities(roleMapper.selectByUserId(user.getId()));
return user;
}
}
Запросите информацию о пользователе через UserMapper, затем инкапсулируйте ее в User и реализуйте интерфейс UserDetails для автоматически сгенерированного User.
User
public class User implements Serializable, UserDetails {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableId(value = "username")
private String username;
@TableId(value = "password")
private String password;
@TableField(exist = false)
private List<Role> authorities;
public User() {
}
public Integer getId() {
return id;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = new BCryptPasswordEncoder().encode(password);
}
public void setAuthorities(List<Role> authorities) {
this.authorities = authorities;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username=" + username +
", password=" + password +
"}";
}
}
объяснять:
Необходимо переписать метод в UserDetails для хранения разрешений пользователей.
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
Таким образом, добавляется новая переменная и аннотируется, чтобы указать, что это не свойство поля.
@TableField(exist = false)
private List<Role> authorities;
Реализуйте интерфейс GrantedAuthority для роли, нужно только имя органа
public class Role implements Serializable, GrantedAuthority {
private static final long serialVersionUID = 1L;
private String name;
@Override
public String toString() {
return name;
}
@Override
public String getAuthority() {
return name;
}
}
Добавьте новый метод в RoleMapper.java для запроса ролей, принадлежащих идентификатору пользователя.
@Select("select name from role r INNER JOIN user_role ur on ur.user_id=1 and ur.role_id=r.id")
List<Role> selectByUserId(Integer id);
контрольная работа
Метод тестирования такой же, как и в первой части, когда токен получен, возврат выглядит следующим образом.
адрес проекта:GitHub.com/На этот раз Шерри, округ Колумбия/…
Ссылка на ссылку:
блог woo woo woo.cn на.com/invoice2952/afraid/89…
nuggets.capable/post/684490…Больше статей смотрите в личном блогеzheyday.github.io/