Я делаю новое веб-приложение, которое разделено на две части:
- API + backoffice (часть Java)
- фронт клиента (часть JS)
Первый проект 1. это классический проект Springroo, использующий MVC и функции безопасности. Его цель - предоставить некоторый пользовательский интерфейс и маршруты администраторам, чтобы они управляли БД.
Они аутентифицируются через классическую форму входа / прохода (автоматически генерируется Springroo), и аутентификация использует сеанс.
Вот содержимое приложенияContext-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<!-- HTTP security configurations -->
<http auto-config="true" use-expressions="true">
<form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t"
/>
<logout logout-url="/resources/j_spring_security_logout" />
<!-- Configure these elements to secure URIs in your application -->
<intercept-url pattern="/users/**" access="hasRole('ADM')" />
<intercept-url pattern="/heroperiods/**" access="hasRole('USER')" />
<intercept-url pattern="/karacters/**" access="hasRole('ADM')" />
<intercept-url pattern="/karacterskillses/**" access="hasRole('ADM')" />
<intercept-url pattern="/karactergpses/**" access="hasRole('ADM')" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/static/**" access="permitAll" />
<intercept-url pattern="/login/**" access="permitAll" />
<intercept-url pattern="/players/login" access="permitAll" />
<intercept-url pattern="/players/new" access="permitAll" />
<intercept-url pattern="/**" access="isAuthenticated()" />
<!-- Concurrent Session Control -->
<session-management session-authentication-error-url="/sessionExpired" >
<concurrency-control max-sessions="1"/>
</session-management>
</http>
<!-- Configure Authentication mechanism -->
<beans:bean name="adminAuthenticationProvider" class="com.ksfadventure.security.AdminAuthenticationProvider">
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="adminAuthenticationProvider" />
</authentication-manager>
</beans:beans>
Эта часть использует AdminAuthenticationProvider, который расширяет AbstractUserDetailsAuthenticationProvider.
public class AdminAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
// TODO Auto-generated method stub
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
String password = authentication.getCredentials().toString();
AccessRight account = AccessRightController.getAccessRight(username, password);
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
if(null == account)
throw new BadCredentialsException("Wrong login or password");
authList.add(new SimpleGrantedAuthority(account.getAccessRightRole()));
return new User(username, account.getAccessRightPassword(), true, true, true, true, authList);
}
}
Все это идет хорошо.
Теперь мне нужно добавить аутентификацию на основе маркеров без сохранения состояния (для входящего проекта JS-клиента), которая будет использоваться конечными пользователями.
@RequestMapping("/players")
@Controller
@RooWebScaffold(path = "players", formBackingObject = Player.class)
public class PlayerController {
private TokenManager tokenManager = new TokenManager();
// (...)
/**
* Authenticate a user
*/
@RequestMapping(value="/login", method=RequestMethod.POST)
public ResponseEntity<String> authenticate(@RequestBody String authParams) {
ObjectMapper mapper = new ObjectMapper();
// (...)
WebServiceAnswer answer = new WebServiceAnswer();
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
// JSON to Object
try {
player = mapper.readValue(authParams, Player.class);
// (...)
} catch (IOException e) {
// (...)
}
if(null == player) return new ResponseEntity<String>(headers, HttpStatus.BAD_REQUEST);
// get and check credentials
email = player.getPlayerMail();
password = player.getPlayerPassword();
player = PlayerController.getPlayer(email, password);
if (null == player) {
// (...)
return new ResponseEntity<String>(answer.toJsonString(), headers, HttpStatus.NOT_FOUND);
}
// generate token
token = tokenManager.generate(player);
// (...)
return new ResponseEntity<String>(answer.toJsonString(), headers, HttpStatus.ACCEPTED);
}
public static Player getPlayer(String email, String password) {
// get the expected account
Player player = Player.findPlayer(email);
if (null == player) return null;
// (...)
String accountPassword = player.getPlayerPassword();
// (...)
// check password
if (BCrypt.checkpw(password, accountPassword)) {
// (...)
return player;
}
// (...)
return null;
} // end of method getAccessRight
}
Вот класс TokenManager.
@Component
public class TokenManager {
private final String secretKey = "ilovestackoverflow";
public String generate(Player player) {
Claims claims = Jwts.claims().setSubject(player.getPlayerMail());
claims.put("playerId", String.valueOf(player.getPlayerId()));
claims.put("role", Roles.getRoleUser());
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, this.secretKey)
.compact();
}
public Player validate(String token) {
Player player = null;
try {
Claims body = Jwts.parser()
.setSigningKey(this.secretKey)
.parseClaimsJws(token)
.getBody();
player = Player.findPlayer(body.getSubject());
// maybe manage a role here ???
// claims.put("role", jwtUser.getRole());
}
catch (Exception e) {
System.out.println(e);
}
return player;
}
}
Чтобы попытаться управлять маршрутами вызовов "/ Players / *" в качестве возможных маршрутов входа в систему, я создал класс TokenConfig.
//@EnableGlobalMethodSecurity(prePostEnabled=true)
@EnableWebSecurity
@Configuration
public class TokenConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PlayerAuthenticationProvider playerAuthenticationProvider;
@Bean
public AuthenticationManager authenticationManager() {
List<AuthenticationProvider> providerList = Collections.singletonList((AuthenticationProvider)playerAuthenticationProvider);
ProviderManager pm = new ProviderManager(providerList);
return pm;
}
//create a custom filter(this is ran at Tomcat start)
@Bean
public TokenFilter authTokenFilter() {
TokenFilter filter = new TokenFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("**/**").authenticated()
.and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
}
Он использует PlayerAuthenticationProvider, который расширяет AbstractUserDetailsAuthenticationProvider.
@Component
public class PlayerAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Autowired
private TokenManager tokenManger;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
// TODO Auto-generated method stub
}
@Override
protected UserDetails retrieveUser(String userMail, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
JwtAuthToken jwtAuthenticationToken = (JwtAuthToken) authentication;
String token = jwtAuthenticationToken.getToken();
Player account = tokenManger.validate(token);
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
if(null == account)
throw new BadCredentialsException("Wrong login or password");
authList.add(new SimpleGrantedAuthority(Roles.getRoleUser()));
return new User(userMail, account.getPlayerPassword(), true, true, true, true, authList);
}
}
И, наконец, TokenFilter.
public class TokenFilter extends AbstractAuthenticationProcessingFilter {
public TokenFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
public TokenFilter() {
super("/**");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
String header = request.getHeader("Authorisation");
if(header == null || !header.startsWith("Check ")) {
throw new RuntimeException(" Token is missing");
}
String authenticationToken = header.substring(6);
JwtAuthToken token = new JwtAuthToken(authenticationToken);
return getAuthenticationManager().authenticate(token);
}
}
Все это позволяет мне получить токен, когда я пытаюсь войти, используя POST-запрос к "/ Players / Login".
Содержимое JSON возвращается, как и ожидалось, но проблема в том, что я не знаю, как управлять этим токеном для будущих вызовов клиента.
Если я пытаюсь достичь другого маршрута (например, / heroperiods / {i}), то сервер проверяет, что у меня нет активного (admin) сеанса, и запрашивает у меня аутентификацию.
Существует ли существующий процесс Java для управления токенами моих клиентов по каждому запросу?
ИЛИ ЖЕ :
Должен ли я открывать все маршруты и управлять ими по одному, вручную? (Я имею в виду проверку правильности токена)
Если так, есть ли глобальное место, где я мог бы проверить статус токена для всех существующих маршрутов?
Должен ли я хранить токен в моей таблице игроков и обновлять его + дату последнего использования?
[EDIT]
Когда я отлаживаю свое приложение, метод TokenConfig.configure () никогда не используется. И если я добавлю аннотацию @EnableGlobalMethodSecurity (prePostEnabled = true), я получу исключение, поскольку вместо одного используется два параметра authenticationManager.
[/ EDIT] * 1 038 *