Домашняя страница блога:me.csdn.net/u010974701
Репозиторий исходного кода:GitHub.com/Округ Чжашуй/…
В предыдущем разделе «Сражение Spring Boot 2.X — вход и регистрация в Spring Security» мы в основном интегрировали Spring Security для реализации регистрации пользователей, входа в систему и контроля разрешений.
В этом разделе мы реализуем контроль доступа к безопасности веб-приложений на основе токенов. Токен Токен — это метод аутентификации без сохранения состояния, он не сохраняет информацию об аутентификации пользователя на стороне сервера, как сеанс, и очень подходит для RESTful API.
Для веб-приложений с чистой аутентификацией Token общая идея такова: на основе предыдущего проекта закройте сеанс, который поставляется с Spring Security, чтобы разрешить междоменные запросы; добавьте перехватчик Token для перехвата всех запросов и проверки того, является ли Token действительный.
Если вы хотите реализовать Session + Token, моя идея состоит в том, чтобы добавить перехватчики Session и Token для перехвата всех запросов. Аутентификация и авторизация сеанса используются для запросов с файлами cookie; запросы без файлов cookie передаются перехватчику Token для аутентификации и авторизации. (Эта идея еще не была реализована, хи-хи, если вы можете, оставьте сообщение и сообщите Xiaoxian).
В этом разделе вместо использования шаблона MVC MySQL и MyBatis используются для создания реализации RESTful API.
1) Что такое токен
1.1) Как сервер узнает, вошел ли пользователь в систему?
Мы знаем, что сам протокол HTTP не включает состояние, и каждый раз, когда HTTP-связь между клиентом и сервером завершается, она отключается, поэтому HTTP не запоминает, кто является вошедшим в систему пользователем.
Чтобы решить ситуацию, когда каждый запрос должен приводить имя пользователя и пароль пользователя для аутентификации пользователя, обычно существуют следующие два метода, каждый из которых имеет свои преимущества и недостатки и может быть выбран в соответствии с фактической ситуацией. проект.
Метод аутентификации на основе сеанса
1. После того, как пользователь входит в систему, информация о входе пользователя (сеанс) создается на стороне сервера, и идентификатор (ID сеанса) информации для входа возвращается клиенту. Клиенты обычно сохраняются с помощью файлов cookie.
2. Клиенту нужно только передать идентификатор регистрационной информации (Session_ID) на сервер для каждого запроса, чтобы выяснить, существует ли соответствующая регистрационная информация (Session).
3. Если информация для входа (Сеанс) найдена, это означает, что проверка пройдена.
Для одного приложения-службы нет проблем, если вы хотите масштабироваться до распределенного приложения. Например, у компании есть две службы, A и B, и ей необходимо понимать, что пользователи могут получить доступ к обеим службам после входа в любую службу. Идея реализации состоит в том, чтобы сохранить сеанс, например, записать в базу данных или кеш Redis, перейти к базе данных или кешу Redis, чтобы найти сеанс во время проверки, регулярно очищать базу данных или устанавливать время истечения срока действия Redis.
Метод аутентификации на основе токенов
В этом методе информация для входа пользователя не хранится на сервере, а токен пользователя хранится на клиенте. Клиент запрашивает информацию о токене, и сервер проверяет действительность токена.
1. Пользователь вводит имя пользователя и пароль пользователя, а после проверки шифрует и генерирует Токен
2. Клиент сохраняет Токен и приносит этот Токен с каждым запросом
3. Сервер проверяет валидность Токена по ключу
Если две службы A и B используют один и тот же алгоритм шифрования и ключ, то токен, сгенерированный любой службой, можно использовать непосредственно в другой службе, даже если добавляется новая служба C, при условии, что алгоритм шифрования и ключ являются то же самое, три токена каждой службы по-прежнему распространены.
Для необходимости аннулировать токен с неистекшим сроком действия после того, как пользователь изменит пароль, вы можете добавить отметку времени для пользователя, чтобы изменить информацию в пользовательской таблице.Пока токен генерируется до этой отметки времени, он будет недействителен по умолчанию; есть еще один метод.Добавьте кеш Redis и удалите кеш токена пользователя после изменения информации о пользователе.Если в кеше нет токена пользователя, он будет недействителен по умолчанию.
Что касается метода настройки единого входа, я сейчас думаю только о кеше Redis (вручную закрой лицо)
1.2) Как выглядит токен?
Токен состоит из трех частей
- Заголовок
- Полезная нагрузка
- Подпись
Полный пример токена с тремя частями, разделенными десятичной точкой.:
eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJTcHJpbmdCb290IiwiYXVkIjoiYm9vdCIsImV4cCI6MTU4NjE5MzMwMywiaWF0IjoxNTg1NTg4NTAzLCJyb2xlIjoiQURNSU4ifQ.SdHSoet9BEaMcBrbbwO4_nd88nO7VIuV6IB_Kdw1AFmmPPCxY8CKUoE-QrJmN3RSMAdxLB0GAfDiwFV6zpxVZksXZQAQzxa_bPw0JUj7mZyHzdSR2jNm_oKB_2rnRsfW7caXZVgwtUU2lHoXSLdlgHRqoyIw7AcP5dm-og3ELGgUQxa27mmwvXtRngfvgw1EKoeA_bdwNSbDWu8clyNjd9ftq9_yU3QKFc3NAUVkWTRa8U1_dyOI9B4LMrKrXEQSR8D7UDw-0MDbOZNwzUmxv0h-QER1cw5dxnQsMs2C9TI32x9E68PaNC8PkaAyOkCs55y-W7wyf-K24fzt5nQb4w
должны знать о том,Заголовок и полезная нагрузка не шифруются, а данные в формате JSON кодируются алгоритмом Bas64, поэтому пароль и информация о конфиденциальности пользователя не могут храниться в токене..
Заголовок
{
"alg": "HS256"
}
Заголовок обычно представляет собой алгоритм шифрования, который объявляет подпись токена. После кодирования Base64 получается первая часть токена. Пример выглядит следующим образом:
eyJhbGciOiJIUzI1NiJ9
Полезная нагрузка
{
"sub": "Joe"
}
Это основная часть Токена, а вот вторая часть Токена, полученная после кодирования Base64, пример такой:
eyJzdWIiOiJKb2UifQ.1KP0SsvENi7Uz1oQc07aXTL7kpQG5jBNIybqr60AlD4
Следует отметить, что полезная нагрузка не зашифрована, пожалуйста, не храните здесь какую-либо личную информацию, такую как пароли, информацию о конфиденциальности пользователей.
Обычно используются следующие настройки, стандартные заявления:
-
setIssuer
: Эмитент токена -
setSubject
: Тема токена -
setAudience
: получатель токена -
setExpiration
: Дата истечения срока годности -
setNotBefore
: После какой даты вступит в силу -
setIssuedAt
: время выпуска -
setId
: уникальный идентификатор
Пользовательские претензии:
-
.claim("role", "ADMIN")
: с помощью этого метода настраивается роль Claims и значение ADMIN.
Подпись
Часть подписи предназначена для токенаЗаголовокиПолезная нагрузкаГенерируется методом шифрования, объявленным в заголовке, и ключом шифрования, определенным сервером, еслиЗаголовоки изменения данных **Payload**,ПодписьСразу после смены, в случае, если ключ не утек, другие людиНевозможно подделать или подделать токен, поэтому Token безопасен.
SdHSoet9BEaMcBrbbwO4_nd88nO7VIuV6IB_Kdw1AFmmPPCxY8CKUoE-QrJmN3RSMAdxLB0GAfDiwFV6zpxVZksXZQAQzxa_bPw0JUj7mZyHzdSR2jNm_oKB_2rnRsfW7caXZVgwtUU2lHoXSLdlgHRqoyIw7AcP5dm-og3ELGgUQxa27mmwvXtRngfvgw1EKoeA_bdwNSbDWu8clyNjd9ftq9_yU3QKFc3NAUVkWTRa8U1_dyOI9B4LMrKrXEQSR8D7UDw-0MDbOZNwzUmxv0h-QER1cw5dxnQsMs2C9TI32x9E68PaNC8PkaAyOkCs55y-W7wyf-K24fzt5nQb4w
JJWT — это набор инструментов с открытым исходным кодом (Apache 2.0) для создания и проверки веб-токенов JSON (JWT) на JVM и Android, а также реализация Java, основанная на спецификациях JWT, JWS, JWE, JWK. Поддерживаемые алгоритмы шифрования: HMAC, RSASSA, ECDSA, и во время разработки необходимо использовать ключ, достаточно надежный для выбранного алгоритма. В этом разделе для шифрования токена будет использоваться асимметричное шифрование RSA.
2) Введение зависимости JJWT и конфигурация проекта
Создайте новый проект 06-security-token,Обратите внимание, что версия Spring Boot должна быть версии 2.1.X., не забудьте проверить Spring Security, MySQL, MyBatis, веб-зависимости.
Для проектов Maven соответствующая конфигурация зависимостей также приведена в конце статьи.
Конфигурация проекта Gradle
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.2'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
// 添加如下的依赖 https://github.com/jwtk/jjwt
compile 'io.jsonwebtoken:jjwt-api:0.11.1'
runtime 'io.jsonwebtoken:jjwt-impl:0.11.1',
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
//'org.bouncycastle:bcprov-jdk15on:1.60',
// or 'io.jsonwebtoken:jjwt-gson:0.11.1' for gson
'io.jsonwebtoken:jjwt-jackson:0.11.1'
1.2) Конфигурация проекта
Настройте базу данных MySQL и преобразование верблюжьего регистра MyBatis, application.properties
# 数据库 URL、用户名、密码、JDBC Driver更换数据库只需更改这些信息即可
# MySQL 8 需要指定 serverTimezone 才能连接成功
spring.datasource.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.password=xiaoxian
spring.datasource.username=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis 驼峰命名转换
mybatis.configuration.map-underscore-to-camel-case=true
Добавить @MapperScan
@MapperScan("org.xian.security.mapper")
public class SecurityApplication {}
2) Начните использовать JJWT
Основная структура проекта:
- пакет контроллера: интерфейс API
- пакет услуг: предоставляет интерфейсные услуги для API
- пакет mapper: класс MyBatis Mapper
- пакет сущностей: класс сущностей
- пакет безопасности: проверка перехвата токена, генерация токена, конфигурация Spring Security
MyResponse : public Response возвращает класс сообщения:
public class MyResponse implements Serializable {
private static final long serialVersionUID = -2L;
private String status;
private String message;
}
2.1) Классы сущностей Entity и Mapper
Структура таблицы здесь такая же, как и в предыдущем разделе, пользовательская таблица sys_user
поле | тип | Примечание |
---|---|---|
user_id | bigint | автоматическое увеличение первичного ключа |
username | varchar(18) | Имя пользователя, ненулевое и уникальное |
password | varchar(60) | пароль, не пустой |
user_role | varchar(8) | Роли пользователей (ПОЛЬЗОВАТЕЛЬ / АДМИНИСТРАТОР) |
Роли пользователей здесь USER/ADMIN, ситуация, когда у пользователя может быть несколько ролей, пока не рассматривается.
SQL
use spring;
create table sys_user
(
user_id bigint auto_increment,
username varchar(18) not null unique,
password varchar(60) not null,
user_role varchar(8) not null,
constraint sys_user_pk
primary key (user_id)
);
Класс сущностей: Создайте новый пакет с именем entity . Создайте новый класс SysUser под сущностью:
public class SysUser implements Serializable {
private static final long serialVersionUID = 4522943071576672084L;
private Long userId;
private String username;
private String password;
private String userRole;
// 省略 getter setter constructor
}
Класс интерфейса картографа: Новый сопоставитель пакетов, новый класс SysUserMapper:
// 这里使用注解的方式
public interface SysUserMapper {
/** 往 sys_user 插入一条记录
* @param sysUser 用户信息
*/
@Insert("Insert Into sys_user(username, password,user_role) Values(#{username}, #{password},#{userRole})")
@Options(useGeneratedKeys = true, keyProperty = "userId")
void insert(SysUser sysUser);
/** 根据用户 Username 查询用户信息
* @param username 用户名
* @return 用户信息
*/
@Select("Select user_id,username, password,user_role From sys_user Where username=#{username}")
SysUser selectByUsername(String username);
}
2.2) Конфигурация токена
Он состоит из следующих частей
- Класс инструмента открытого ключа RSA key
- Класс инструментов генерации и проверки токенов
- Перехватчик токенов, перехватывает все запросы и проверяет, что токен действителен.
- Custom не реализует класс обработки ошибок авторизованного доступа
- Пользовательская реализация интерфейса UserDetailsService
- Конфигурация безопасности Spring
Создайте новый класс RsaUtils в пакете Security, класс инструмента для открытых и секретных ключей RSA. Обратите внимание, что в JDK 8 2048-битные ключи не поддерживаются.
public class RsaUtils {
/** PrivateKey * 生成秘钥 > openssl genrsa -out rsa_private_key.pem 2048
* 转换成PKCS8格式 >openssl pkcs8 -topk8 -inform * PEM -in rsa_private_key.pem -outform PEM -nocrypt
* 在终端输出结果,去掉“-----BEGIN PRIVATE KEY-----” * “-----END PRIVATE KEY-----”
* @return PrivateKey
*/
public static PrivateKey getPrivateKey() {
PrivateKey privateKey = null;
try {
String privateKeyStr = "PrivateKey";
// PKCS8格式的密钥
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr));
// RSA 算法
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
return privateKey;
}
/** PublicKey 根据 秘钥 生成public key > openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
* @return PublicKey
*/
public static PublicKey getPublicKey() {
PublicKey publicKey = null;
try {
String publicKeyStr = "public key";
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
}
}
TokenUtils: служебный класс для создания и проверки токенов. Необязательная часть тела Token означает, что эта информация не используется для аутентификации и авторизации:
@Component
public class TokenUtils implements Serializable {
private static final long serialVersionUID = -3L;
/**
* Token 有效时长
*/
private static final Long EXPIRATION = 604800L;
/** 生成 Token 字符串 必须 setAudience 接收者 setExpiration 过期时间 role 用户角色
* @param sysUser 用户信息
* @return 生成的Token字符串 or null
*/
public String createToken(SysUser sysUser) {
try {
// Token 的过期时间
Date expirationDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
// 生成 Token
String token = Jwts.builder()
// 设置 Token 签发者 可选
.setIssuer("SpringBoot")
// 根据用户名设置 Token 的接受者
.setAudience(sysUser.getUsername())
// 设置过期时间
.setExpiration(expirationDate)
// 设置 Token 生成时间 可选
.setIssuedAt(new Date())
// 通过 claim 方法设置一个 key = role,value = userRole 的值
.claim("role", sysUser.getUserRole())
// 设置加密密钥和加密算法,注意要用私钥加密且保证私钥不泄露
.signWith(RsaUtils.getPrivateKey(), SignatureAlgorithm.RS256)
.compact();
return String.format("Bearer %s", token);
} catch (Exception e) {
return null;
}
}
/** 验证 Token ,并获取到用户名和用户权限信息
* @param token Token 字符串
* @return sysUser 用户信息
*/
public SysUser validationToken(String token) {
try {
// 解密 Token,获取 Claims 主体
Claims claims = Jwts.parserBuilder()
// 设置公钥解密,以为私钥是保密的,因此 Token 只能是自己生成的,如此来验证 Token
.setSigningKey(RsaUtils.getPublicKey())
.build().parseClaimsJws(token).getBody();
assert claims != null;
// 验证 Token 有没有过期 过期时间
Date expiration = claims.getExpiration();
// 判断是否过期 过期时间要在当前日期之后
if (!expiration.after(new Date())) {
return null;
}
SysUser sysUser = new SysUser();
sysUser.setUsername(claims.getAudience());
sysUser.setUserRole(claims.get("role").toString());
return sysUser;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
TokenFilter: Перехватчик токенов, который перехватывает все запросы и проверяет, действителен ли токен. Если он действителен, авторизация проходит. Если он недействителен, Spring Security будет перехватывать недопустимые запросы в соответствии с конфигурацией:
@SuppressWarnings("SpringJavaAutowiringInspection")
@Service
public class TokenFilter extends OncePerRequestFilter {
@Resource
TokenUtils tokenUtils;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 存储 Token 的 Headers Key与 Value,默认是 Authorization
final String authorizationKey = "Authorization";
String authorizationValue;
try {
authorizationValue = request.getHeader(authorizationKey);
} catch (Exception e) {
authorizationValue = null;
}
// Token 开头部分 默认 Bearer 开头
String bearer = "Bearer ";
if (authorizationValue != null && authorizationValue.startsWith(bearer)) {
// token
String token = authorizationValue.substring(bearer.length());
SysUser sysUser = tokenUtils.validationToken(token);
if (sysUser != null) {
// Spring Security 角色名称默认使用 "ROLE_" 开头
// authorities.add 可以增加多个用户角色,对于一个用户有多种角色的系统来说,
// 可以通过增加用户角色表、用户--角色映射表,存储多个用户角色信息
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + sysUser.getUserRole()));
// 传入用户名、用户密码、用户角色。 这里的密码随便写的,用不上
UserDetails userDetails = new User(sysUser.getUsername(), "password", authorities);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(userDetails.getUsername());
// 授权
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
ErrorAuthenticationEntryPoint: Класс сообщения об ошибке, неавторизованный доступ возвращает 401 информацию о неавторизованном доступе через этот класс. Пользовательская реализация стандартного интерфейса обработки несанкционированного доступа Spring Security AuthenticationEntryPoint:
@Component
public class ErrorAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = 5200068540912465653L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 设置 Json 格式返回
response.setContentType("application/json;charset=UTF-8");
// 设置 HTTP 状态码为 401
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// PrintWriter 输出 Response 返回信息
PrintWriter writer = response.getWriter();
ObjectMapper mapper = new ObjectMapper();
MyResponse myResponse = new MyResponse("error", "非授权访问");
// 将对象输出为 JSON 格式。可以通过重写 MyResponse 的 toString() ,直接通过 myResponse.toString() 即可
writer.write(mapper.writeValueAsString(myResponse));
}
}
UserDetailsServiceImpl: Пользовательская реализация UserDetailsService путем переопределения метода loadUserByUsername интерфейса UserDetailsService для передачи имени пользователя, пароля пользователя и роли пользователя в Spring Security.
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Resource
private SysUserMapper sysUserMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserMapper.selectByUsername(username);
if (sysUser == null ) {
throw new UsernameNotFoundException(username);
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
// Spring Security 角色名称默认使用 "ROLE_" 开头
// authorities.add 可以增加多个用户角色,对于一个用户有多种角色的系统来说,
// 可以通过增加用户角色表、用户--角色映射表,存储多个用户角色信息
authorities.add(new SimpleGrantedAuthority("ROLE_" + sysUser.getUserRole()));
// 给 Spring Security 传入用户名、用户密码、用户角色。
return new User(sysUser.getUsername(), sysUser.getPassword(), authorities);
}
}
SpringSecurityConfig: Конфигурация Spring Security, настройка алгоритма шифрования хранилища паролей, добавление перехватчика, закрытие диспетчера сеансов, разрешение междоменного доступа, разрешение API входа и регистрации без авторизации.
@SuppressWarnings("SpringJavaAutowiringInspection")
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserDetailsServiceImpl userDetailsService;
@Resource
private ErrorAuthenticationEntryPoint errorAuthenticationEntryPoint;
@Resource
private TokenFilter tokenFilter;
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
// 使用 BCryptPasswordEncoder 验证密码
authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
// BCrypt 密码
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
// 配置 CSRF 关闭,允许跨域访问
httpSecurity.csrf().disable();
// 指定错误未授权访问的处理类
httpSecurity.exceptionHandling().authenticationEntryPoint(errorAuthenticationEntryPoint);
// 关闭 Session
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 允许 登录 注册的 api 的无授权访问,其他需要授权访问
httpSecurity.authorizeRequests()
.antMatchers("/api/user/login", "/api/user/register")
.permitAll().anyRequest().authenticated();
// 添加拦截器
httpSecurity.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
// 禁用缓存
httpSecurity.headers().cacheControl();
}
}
На этом настройка безопасности токена завершена.
2.3) Интерфейс API и сервисный уровень интерфейса
Создайте новый пакет службы и создайте новый слой службы интерфейса SysUserService:
@Service
public class SysUserService {
@Resource
private AuthenticationManager authenticationManager;
@Resource
private TokenUtils tokenUtils;
@Resource
private SysUserMapper sysUserMapper;
/** 用户登录
* @param sysUser 用户登录信息
* @return 用户登录成功返回的Token
*/
public MyResponse login(final SysUser sysUser) {
try {
// 验证用户名和密码是否对的
System.out.println(sysUser.getUsername());
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(sysUser.getUsername(),
sysUser.getPassword()));
} catch (BadCredentialsException e) {
return new MyResponse("ERROR", "用户名或者密码不正确");
}
// 生成Token与查询用户权限
SysUser sysUserData = sysUserMapper.selectByUsername(sysUser.getUsername());
return new MyResponse("SUCCESS",
tokenUtils.createToken(sysUserData));
}
/** 用户注册
* @param sysUser 用户注册信息
* @return 用户注册结果
*/
public MyResponse save(SysUser sysUser) throws DataAccessException {
try {
// 密码加密存储
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String password = bCryptPasswordEncoder.encode(sysUser.getPassword());
sysUser.setPassword(password);
sysUserMapper.insert(sysUser);
} catch (DataAccessException e) {
return new MyResponse("ERROR", "已经存在该用户名或者用户昵称,或者用户权限出错");
}
return new MyResponse("SUCCESS", "用户新增成功");
}
}
SysUserController: Интерфейс RESTful API, @PreAuthorize("hasRole('ADMIN')") указывает, что только пользователи с полномочиями ADMIN могут получить доступ
@RestController
@RequestMapping(value = "/api/user")
public class SysUserController {
@Resource
private SysUserService sysUserService;
/** 用户登录接口
* @param sysUser 用户登录的用户名和密码
* @return 用户Token和角色
* @throws AuthenticationException 身份验证错误抛出异常
*/
@PostMapping(value = "/login")
public MyResponse login(@RequestBody final SysUser sysUser) throws AuthenticationException {
return sysUserService.login(sysUser);
}
/** 用户注册接口
* @param sysUser 用户注册信息
* @return 用户注册结果
*/
@PostMapping(value = "/register")
public MyResponse register(@RequestBody @Valid final SysUser sysUser) {
return sysUserService.save(sysUser);
}
/** 这是登录用户才可以看到的内容 */
@PostMapping(value = "/message")
public String message() {
return "这个消息只有登录用户才可以看到";
}
/** 这是管理员用户才可以看到 */
@PostMapping(value = "/admin")
@PreAuthorize("hasRole('ADMIN')")
public String admin() {
return "这个消息只有管理员用户才可以看到";
}
}
2.4) Бежать
Запустите проект, чтобы зарегистрироваться, войти в систему и получить доступ к пользовательскому интерфейсу авторизации соответственно.
регистр:Зарегистрируйте пользователей с правами ADMIN и USER соответственно.
Авторизоваться:
Авторизоваться с помощью токена: /api/user/admin и /api/user/message
Приложение, конфигурация проекта Maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
<!-- Uncomment this next dependency if you are using JDK 10 or earlier and you also want to use
RSASSA-PSS (PS256, PS384, PS512) algorithms. JDK 11 or later does not require it for those algorithms:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
<scope>runtime</scope>
</dependency> -->
</dependencies>
Этот раздел в основном посвящен интеграции Spring Boot с интерфейсом Spring Security, JJWT, RESTful API для аутентификации и авторизации токена, а также функциям регистрации пользователей, входа в систему и управления ролями. В следующем разделе мы интегрируем другую часто используемую структуру безопасности с Spring Boot.Apache ShiroИнтерфейс RESTful API, который реализует аутентификацию и авторизацию с помощью токена. Следующая последовательность статей организована
- Apache Shiro реализует RESTful API для аутентификации и авторизации токенов.
- Apache Shiro реализует вход в систему с помощью скан-кода WeChat