Я использую Spring Boot2 в качестве фреймворка и Thymeleaf в качестве механизма шаблонов.
на моем сервере авторизации я добавил пользователя «admin» как «ROLE_ADMIN».
но в клиентском приложении, когда я вошел в систему как «администратор» и распечатал Authentication Object from SecurityContextHolder.getContext().getAuthentication(), свойство Granted Authorities имеет только «ROLE_USER».
Ниже приведена конфигурация моего сервера авторизации.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password(passwordEncoder().encode("123")).roles("USER", "ADMIN");
auth
.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("123")).roles("USER");
}
а ниже Authentication Объект из кода регистрации SecurityContextHolder.getContext().getAuthentication().
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth.isAuthenticated());
System.out.println(auth.getAuthorities());
System.out.println(auth.getPrincipal());
и результат
// isAuthenticated()
true
// getAuthorites()
[ROLE_USER]
// getPrincipal()
Name: [admin], Granted Authorities: [ROLE_USER], User Attributes: [authorities=[{authority=ROLE_ADMIN}, {authority=ROLE_USER}], ...
Ниже приведен мой код тимелеафа.
<div sec:authorize = "isAuthenticated()">
Text visible only to authenticated users.
<!-- Principal name -->
Authenticated username:
<div sec:authentication = "name"></div>
<div sec:authorize = "hasRole('USER')">Text visible to user.</div>
<!-- i cant see this message -->
<div sec:authorize = "hasRole('ADMIN')">Text visible to admin.</div>
Authenticated user roles:
<!-- print '[ROLE_USER]' only -->
<div sec:authentication = "principal.authorities"></div>
</div>
<div sec:authorize = "!isAuthenticated()">Text visible only to
unauthenticated users.
</div>
Итак, я хочу получить доступ к Principal.UserAttributes.authorities в тимелеафе.
я имею в виду OAuth2AuthenticationToken, OAuth2User.getAttributes() и DefaultOAuth2User.toString()
Как я могу это сделать?
@МаксР. спасибо за Ваш ответ. тогда я должен добавить параметр Principal ко всем контроллерам, которые включают тимелеаф, используя тимелеаф-безопасность?
Да, это был бы один из способов.
@МаксР. Ok. я понял. но я не знаю, почему у моего Principal объекта еще нет ROLE_ADMIN. в конфигурации сервера авторизации я добавил пользователя «admin» с «ROLE_ADMIN»
Вам нужно будет извлечь роли из ваших [полномочий] и добавить их в предоставленные полномочия самостоятельно, один из способов — использовать интерфейс AuthoritiesExtractor, вот пример: baeldung.com/… (в 4.1 есть пример реализации AuthoritiesExtractor)




Вы можете передать Принципал своему контроллеру в качестве аргумента, например
public String myController(Principal principal) {
...
}
Вам также придется сопоставить полномочия с предоставленными полномочиями самостоятельно, например. используя интерфейс AuthoritiesExtractor из Spring, вот пример: Ссылка из Баелдунга
спасибо за Ваш ответ. я не хочу реализовывать параметризованный контроллер. слишком много контроллеров используют эту функцию. но как вы сказали, я проверю AuthoritiesExtractor. Спасибо.
ищу AuthoritiesExtractor. мой проект состоит из Client, Authorization Server и Resource Server. эти проекты разделены. какой проект реализовать CustomAuthoritiesExtractor? Я добавил весь проект, но не работает. в Resource Server я регистрирую Principal или Authentication объект, в котором отображается конечная точка «/me». и есть «ROLE_USER», «ROLE_ADMIN» в Granted Authorities правильно. я думаю, что что-то не так, когда получаю Granted Authorities в клиенте
Использовать объект #authentication
<div th:text = "${#authentication.principal.something}">
The value of the "name" property of the authentication object should appear here.
</div>
Пример:
<img th:if = "${#authentication.principal.image}"
class = "img-circle" th:src = "${#authentication.principal.image}"
width = "100" height = "100" alt = "place-holder" />
Но сначала добавьте эту зависимость
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>-latest-version-here-</version>
</dependency>
Поскольку он не поставляется с тимелеаф-стартером в весенней загрузке
спасибо за Ваш ответ. я уже добавил thymeleaf-extras-springsecurity5 зависимость. что я хочу сказать, так это «почему мой основной объект имеет полномочия только« ROLE_USER ».
См. реализацию DefaultOAuth2UserService. Реализация по умолчанию всегда создает объект Prinicpal с полномочиями USER_ROLE. Вы должны предоставить собственную реализацию OAuth2UserService
Set<GrantedAuthority> authorities = Collections.singleton(new OAuth2UserAuthority(userAttributes));public OAuth2UserAuthority(Map<String, Object> attributes) { this("ROLE_USER", attributes); }Я решил.
На сервере авторизации я настроил так.
WebSecurityConfigurerAdapter@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password(passwordEncoder().encode("123")).roles("USER", "ADMIN").authorities("USER", "ADMIN");
auth
.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("123")).roles("USER");
}
...
}
и ниже приведен контроллер сопоставления /me моего сервера ресурсов
/me сопоставленный контроллер@RestController
public class UserController {
@RequestMapping("/me")
public Principal user(Principal principal) {
return principal;
}
}
и ниже приведена конфигурация моего клиента WebSecurityConfigurerAdapter
WebSecurityConfigurerAdapter@Configuration
@EnableOAuth2Client
public class WebSecurityConfigurerAdapterImpl extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll()
.anyRequest().authenticated()
.and().oauth2Login();
}
и в клиентском контроллере я зарегистрировался так.
Principal в клиентском контроллере @GetMapping("")
public String git1() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth.getPrincipal());
/** Thymeleaf using this **/
Object authenticationProperty = AuthUtils.getAuthenticationProperty(auth, "principal.attributes['authorities']");
System.out.println(authenticationProperty.toString());
return VIEW_PATH + "git1";
}
и далее результат
Name: [admin], Granted Authorities: [ROLE_USER], User Attributes: [authorities=[{authority=USER}, {authority=ADMIN}], details = {remoteAddress=127.0.0.1, sessionId=null, tokenValue=82a7a532-a31e-4d0a-bd83-f15a9cbea3bc, tokenType=Bearer, decodedDetails=null}, authenticated=true, userAuthentication = {authorities=[{authority=USER}, {authority=ADMIN}], details=null, authenticated=true, principal=admin, credentials=N/A, name=admin}, oauth2Request = {clientId=foo, scope=[read], requestParameters = {client_id=foo}, resourceIds=[], authorities=[], approved=true, refresh=false, redirectUri=null, responseTypes=[], extensions = {}, refreshTokenRequest=null, grantType=null}, clientOnly=false, principal=admin, credentials=, name=admin]
[{authority=USER}, {authority=ADMIN}]
как видите, я добавил полномочия «ROLE_USER» и «ROLE_ADMIN» на сервере авторизации.
в объекте Resource Server Principal предоставлены как «ROLE_ADMIN», так и «ROLE_USER».
но в клиентском Principal объекте не предоставлена «ROLE_ADMIN». есть только 'ROLE_USER'.
и Principal.atttibutes['authorities'] имеет «ПОЛЬЗОВАТЕЛЬ», «АДМИН».
как сказал @Rahil Husain, есть DefaultOAuth2UserService, и эта служба предоставляет «ROLE_USER» только OAuth2User Object.
во-первых, я добавил CustomAuthoritiesExtractor через аннотацию @Componenet (тоже @Bean) в клиент.
но это не работает в моих проектах.
Итак, я реализовал CustomOAuth2User и CustomOAuth2UserService.
так.
CustomOAuth2Userpublic class CustomOAuth2User implements OAuth2User {
private List<GrantedAuthority> authorities;
private Map<String, Object> attributes;
private String name;
public CustomOAuth2User(List<GrantedAuthority> authorities, Map<String, Object> attributes) {
this.authorities = authorities;
this.attributes = attributes;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public Map<String, Object> getAttributes() {
if (this.attributes == null) {
this.attributes = new HashMap<>();
this.attributes.put("name", this.getName());
}
return attributes;
}
@Override
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
и следующее CustomOAuth2UserService
CustomOAuth2UserServicepublic class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
AuthoritiesExtractor authoritiesExtractor = new CustomAuthoritiesExtractor();
List<GrantedAuthority> grantedAuthorityList = authoritiesExtractor.extractAuthorities(oAuth2User.getAttributes());
CustomOAuth2User customOAuth2User = new CustomOAuth2User(grantedAuthorityList, oAuth2User.getAttributes());
customOAuth2User.setName(oAuth2User.getName());
return customOAuth2User;
}
}
и ниже мой CustomAuthoritiesExtractor. этот класс не используется как @Bean или @Component. непосредственно используется в CustomOAuth2Service для сопоставления CustomOAuth2User полномочий объекта
CustomAuthoritiesExtractorpublic class CustomAuthoritiesExtractor implements AuthoritiesExtractor {
@Override
public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
return AuthorityUtils.commaSeparatedStringToAuthorityList(asAuthorities(map));
}
private String asAuthorities(Map<String, Object> map) {
List<String> authorities = new ArrayList<>();
List<LinkedHashMap<String, String>> authz =
(List<LinkedHashMap<String, String>>) map.get("authorities");
for (LinkedHashMap<String, String> entry : authz) {
authorities.add(entry.get("authority"));
}
return String.join(",", authorities);
}
}
и, наконец, я изменил конечную точку клиента на использование моих CustomOAuth2User и CustomOAuth2UserService.
Итак, я изменил конфигурацию клиента WebSecurityConfigurerAdapter следующим образом.
@Configuration
@EnableOAuth2Client
public class WebSecurityConfigurerAdapterImpl extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll()
.anyRequest().authenticated()
.and().oauth2Login()
/** add this config**/
.userInfoEndpoint()
.customUserType(CustomOAuth2User.class, "teemo")
.userService(this.oauth2UserService());
}
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
return new CustomOAuth2UserService();
}
и ниже мой тимелеаф.
<div sec:authorize = "isAuthenticated()">
Text visible only to authenticated users.
Authenticated username:
<div sec:authentication = "name"></div>
<div sec:authorize = "hasRole('USER')">hasRole('USER')</div>
<div sec:authorize = "hasRole('ROLE_USER')">hasRole('ROLE_USER')</div>
<div sec:authorize = "hasRole('ADMIN')">hasRole('ADMIN')</div>
<div sec:authorize = "hasRole('ROLE_ADMIN')">hasRole('ROLE_ADMIN')</div>
<!-- TRUE -->
<div sec:authorize = "hasAuthority('USER')">hasAuthority('USER')</div>
<div sec:authorize = "hasAuthority('ROLE_USER')">hasAuthority('ROLE_USER')</div>
<!-- TRUE -->
<div sec:authorize = "hasAuthority('ADMIN')">hasAuthority('ADMIN')</div>
<div sec:authorize = "hasAuthority('ROLE_ADMIN')">hasAuthority('ROLE_ADMIN')</div>
</div>
<div sec:authorize = "!isAuthenticated()">Text visible only to
unauthenticated users.
</div>
и следующий результат.
Text visible only to authenticated users. Authenticated username:
admin
hasAuthority('USER')
hasAuthority('ADMIN')
всем, кто копает, как я, я надеюсь помочь этот вопрос и ответы.
но я не знаю, что это де-факто стандартный способ.
просто.. работаю сейчас.
вау, это было потрясающе, но мне пришлось заменить метод asAuthorities на String authoritiesString= map.get("authorities").toString(); authoritiesString = authoritiesString.substring(1, authoritiesString.length() - 1); authoritiesString = authoritiesString.trim(); return authoritiesString;, так как приведение к LinkedHashMap не сработало. но спасибо ты спас мой день
В вашем контроллере добавьте принципала в качестве аргумента, например.
public String myController(Principal principal){ ... }, а затем добавьте его на карту вашей модели (или класс) и затем используйте его в тимелеафе.